shell - Custom bash completion obtaining from help text - Stack Overflow

admin2025-04-18  6

For some reason Bash completion doesn't work when I obtain from help text.

This is my C++ file compiled and placed as dbcmd executable file in one of the PATHs:

$ cat dbcmd.cpp
#include <iostream>
#include <string>
#include <vector>

static void usage()
{
        std::cerr << "Usage: dbcmd [<command> [<command-args>]]\n";
        std::cerr << "Commands:\n";

        std::vector<std::string> commands;

        commands.push_back("backup");
        commands.push_back("db-backup");
        commands.push_back("db-purge");
        commands.push_back("load");
        commands.push_back("debug");
        commands.push_back("analyze");
        commands.push_back("cycle");
        commands.push_back("endday");
        commands.push_back("gendbset");
        commands.push_back("help");
        commands.push_back("perform");
        commands.push_back("print");
        commands.push_back("remove");
        commands.push_back("restart");
        commands.push_back("dump");
        commands.push_back("start");
        commands.push_back("status");
        commands.push_back("stop");
        commands.push_back("tables");
        commands.push_back("info");
        commands.push_back("update");
        for (auto command: commands)
                std::cerr << "  " << command << "\n";
}

int main()
{
        usage();
        return 0;
}

And this is my Bash completion script (credit):

$ cat dbcmd_completion.bash
#/usr/bin/env bash
_dbcmd_completions()
{
        if [ "${#COMP_WORDS[@]}" != "2" ]; then
                return
        fi

        local IFS=$'\n'
        local list_var="$(dbcmd|awk -F '\t' '{printf("%s", $1)}'|sed 's/.*://'|sed 's/^\s.//'|sed 's/\s\+/ /g')"
        local suggestions=($(compgen -W "$list_var" -- "${COMP_WORDS[1]}"))

        if [ "${#suggestions[@]}" == "1" ]; then
                local number="${suggestions[0]/%\ */}"
                COMPREPLY=("$number")
        else
                for i in "${!suggestions[@]}"; do
                        suggestions[$i]="$(printf '%*s' "-$COLUMNS"  "${suggestions[$i]}")"
                done
                COMPREPLY=("${suggestions[@]}")
        fi
}

_dothis_completions()
{
        if [ "${#COMP_WORDS[@]}" != "2" ]; then
                return
        fi
        local list_var="backup db-backup db-purge load debug analyze cycle endday gendbset help perform print remove restart dump start status stop tables info update"
        local suggestions=($(compgen -W "$list_var" -- "${COMP_WORDS[1]}"))

        if [ "${#suggestions[@]}" == "1" ]; then
                local number="${suggestions[0]/%\ */}"
                COMPREPLY=("$number")
        else
                for i in "${!suggestions[@]}"; do
                        suggestions[$i]="$(printf '%*s' "-$COLUMNS"  "${suggestions[$i]}")"
                done
                COMPREPLY=("${suggestions[@]}")
        fi
}
#complete -F _dothis_completions dbcmd
complete -F _dbcmd_completions dbcmd

When I uncomment and use _dothis_completions it works perfectly, but _dbcmd_completions--which obtains completion list from help text of dbcmd--doesn't work. When TAB is pressed it produces weird behaviour.

When you execute echo $list_var basically in both of the functions produce the same result. Why doesn't Bash completion like awk/sed produced result. This is on RH 9.

For some reason Bash completion doesn't work when I obtain from help text.

This is my C++ file compiled and placed as dbcmd executable file in one of the PATHs:

$ cat dbcmd.cpp
#include <iostream>
#include <string>
#include <vector>

static void usage()
{
        std::cerr << "Usage: dbcmd [<command> [<command-args>]]\n";
        std::cerr << "Commands:\n";

        std::vector<std::string> commands;

        commands.push_back("backup");
        commands.push_back("db-backup");
        commands.push_back("db-purge");
        commands.push_back("load");
        commands.push_back("debug");
        commands.push_back("analyze");
        commands.push_back("cycle");
        commands.push_back("endday");
        commands.push_back("gendbset");
        commands.push_back("help");
        commands.push_back("perform");
        commands.push_back("print");
        commands.push_back("remove");
        commands.push_back("restart");
        commands.push_back("dump");
        commands.push_back("start");
        commands.push_back("status");
        commands.push_back("stop");
        commands.push_back("tables");
        commands.push_back("info");
        commands.push_back("update");
        for (auto command: commands)
                std::cerr << "  " << command << "\n";
}

int main()
{
        usage();
        return 0;
}

And this is my Bash completion script (credit):

$ cat dbcmd_completion.bash
#/usr/bin/env bash
_dbcmd_completions()
{
        if [ "${#COMP_WORDS[@]}" != "2" ]; then
                return
        fi

        local IFS=$'\n'
        local list_var="$(dbcmd|awk -F '\t' '{printf("%s", $1)}'|sed 's/.*://'|sed 's/^\s.//'|sed 's/\s\+/ /g')"
        local suggestions=($(compgen -W "$list_var" -- "${COMP_WORDS[1]}"))

        if [ "${#suggestions[@]}" == "1" ]; then
                local number="${suggestions[0]/%\ */}"
                COMPREPLY=("$number")
        else
                for i in "${!suggestions[@]}"; do
                        suggestions[$i]="$(printf '%*s' "-$COLUMNS"  "${suggestions[$i]}")"
                done
                COMPREPLY=("${suggestions[@]}")
        fi
}

_dothis_completions()
{
        if [ "${#COMP_WORDS[@]}" != "2" ]; then
                return
        fi
        local list_var="backup db-backup db-purge load debug analyze cycle endday gendbset help perform print remove restart dump start status stop tables info update"
        local suggestions=($(compgen -W "$list_var" -- "${COMP_WORDS[1]}"))

        if [ "${#suggestions[@]}" == "1" ]; then
                local number="${suggestions[0]/%\ */}"
                COMPREPLY=("$number")
        else
                for i in "${!suggestions[@]}"; do
                        suggestions[$i]="$(printf '%*s' "-$COLUMNS"  "${suggestions[$i]}")"
                done
                COMPREPLY=("${suggestions[@]}")
        fi
}
#complete -F _dothis_completions dbcmd
complete -F _dbcmd_completions dbcmd

When I uncomment and use _dothis_completions it works perfectly, but _dbcmd_completions--which obtains completion list from help text of dbcmd--doesn't work. When TAB is pressed it produces weird behaviour.

When you execute echo $list_var basically in both of the functions produce the same result. Why doesn't Bash completion like awk/sed produced result. This is on RH 9.

Share Improve this question edited Feb 25 at 22:34 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Jan 29 at 23:30 Ramanan TRamanan T 4674 silver badges13 bronze badges 10
  • Is that really the entire contents of the dbcmd file? Why does it need the echo statement -- it could just contain the list of commands as a text file. – Barmar Commented Jan 29 at 23:42
  • Don't forget to remove the " on the last line. – Barmar Commented Jan 29 at 23:45
  • You don't describe "weird behaviour" more closely, so it's hard to tell what you mean. The if/else` block at the end of the function injects spaces, which seems unnecessary, but I don't know what the indented behaviour is. – Benjamin W. Commented Jan 29 at 23:55
  • @BenjaminW. Basically it doesn't complete the commands. it is all over the command prompt with TABs & spaces and cannot backspace and delete. Quite hard to describe. It would be easer if you use the script for completion and run dbcmd TAB TAB. This is straight forward script – Ramanan T Commented Jan 30 at 0:29
  • Not resetting IFS improves things for me. – Benjamin W. Commented Jan 30 at 2:04
 |  Show 5 more comments

2 Answers 2

Reset to default 0

It seems using source works :

local list_var="$(source dbcmd|awk -F '\t' '{printf("%s", $1)}'|sed 's/.*://'|sed 's/^\s.//'|sed 's/\s\+/ /g')"

Finally able to solve this. Things to note are:

  1. Removed IFS
  2. redirecting stderr output to stdout

Working script is:

$ cat dbcmd_completion.bash
#/usr/bin/env bash
_dbcmd_completions()
{
        if [ "${#COMP_WORDS[@]}" != "2" ]; then
                return
        fi
        local list_var="$(dbcmd 2>&1|awk -F '\t' '{printf("%s", $1)}'|sed 's/.*://'|sed 's/^\s.//'|sed 's/\s\+/ /g')"
        local suggestions=($(compgen -W "$list_var" -- "${COMP_WORDS[1]}"))
        if [ "${#suggestions[@]}" == "1" ]; then
                local number="${suggestions[0]/%\ */}"
                COMPREPLY=("$number")
        else
                for i in "${!suggestions[@]}"; do
                        suggestions[$i]="$(printf '%*s' "-$COLUMNS"  "${suggestions[$i]}")"
                done
                COMPREPLY=("${suggestions[@]}")
        fi
}

complete -F _dbcmd_completions dbcmd
转载请注明原文地址:http://www.anycun.com/QandA/1744945029a89844.html