Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bash autocompletion: add description for possible completions

Is it possible to make bash auto-completion look like in Cisco IOS shell?

I mean to add short descriptions for each completion, like this:

telnet 10.10.10. (TAB Pressed)
 10.10.10.10 - routerA
 10.10.10.11 - routerB

where 10.10.10.10 and 10.10.10.11 are possible completions and routerA & routerB just descriptions (not to be executed).

I know that bash can complete commands with "complete -W", but is it able to print descriptions for them?

like image 257
DDJ Avatar asked Sep 01 '11 07:09

DDJ


People also ask

How do you autocomplete in bash?

Bash completion is a bash function that allows you to auto complete commands or arguments by typing partially commands or arguments, then pressing the [Tab] key. This will help you when writing the bash command in terminal.

How do you add a tab completion to a bash script?

If you want to enable the completion for all users, you can just copy the script under /etc/bash_completion. d/ and it will automatically be loaded by Bash.

How do I use autocomplete command in Linux?

When at the MS-DOS, Windows command line or a Linux or Unix shell, typing in long file names or directories can become a burden. Use the Tab to autocomplete the names of directories and files while in the command line.

How do you implement tab completion?

If you want completion within your program, you normally have to find matches starting with a particular string. This might be done by populating all possible values in a std::map, then using lower_bound and/or upper_bound to find the currently matching completions.


2 Answers

I have a solution to this that does not require pressing TAB more than twice or echoing any extra information. The key is to check whether there is only one completion, then strip that completion down to the valid portion, usually by removing the largest matching suffix after your "comment" delimiter. To accomplish the OP's example:

_telnet() {
  COMPREPLY=()
  local cur
  cur=$(_get_cword)
  local completions="10.10.10.10 - routerA
10.10.10.11 - routerB
10.20.1.3 - routerC"

  local OLDIFS="$IFS"
  local IFS=$'\n'
  COMPREPLY=( $( compgen -W "$completions" -- "$cur" ) )
  IFS="$OLDIFS"
  if [[ ${#COMPREPLY[*]} -eq 1 ]]; then #Only one completion
    COMPREPLY=( ${COMPREPLY[0]%% - *} ) #Remove ' - ' and everything after
  fi
  return 0
}
complete -F _telnet -A hostnames telnet

This gives the exact output you're looking for, and when there is only one possible completion, the comment is stripped from it before completing.

like image 190
bonsaiviking Avatar answered Oct 04 '22 05:10

bonsaiviking


I'd use conversion based on whether the number of candidates become one (as shown by @bonsaiviking) for simple cases and the following if I needed more flexibility in what I want to show the user.

__foo () {
    local WORDS
    WORDS=("1|10.10.10.10|routerA" "2|10.10.10.11|routerB")

    local FOR_DISPLAY=1
    if [ "${__FOO_PREV_LINE:-}" != "$COMP_LINE" ] ||
            [ "${__FOO_PREV_POINT:-}" != "$COMP_POINT" ]; then
        __FOO_PREV_LINE=$COMP_LINE
        __FOO_PREV_POINT=$COMP_POINT
        FOR_DISPLAY=
    fi

    local IFS=$'\n'
    COMPREPLY=($(
        for WORD in "${WORDS[@]}"; do
            IFS=\| read -ra SP <<<"$WORD"
            if [ "${SP[1]:0:${#2}}" == "$2" ]; then
                if [ -n "$FOR_DISPLAY" ]; then
                    printf "%-*s\n" "$COLUMNS" "${SP[0]}: ${SP[1]} - ${SP[2]}"
                else
                    echo "${SP[1]}"
                fi
            fi
        done
    ))
}
complete -F __foo x

Note: You could probably use COMP_TYPE to set FOR_DISPLAY in Bash 4.x but I needed to support Bash 3.x as well.

This behaves as follows:

$ x 1

Tab

$ x 10.10.10.1

TabTab

1: 10.10.10.10 - routerA
2: 10.10.10.11 - routerB
$ x 10.10.10.1
like image 43
antak Avatar answered Oct 04 '22 06:10

antak