bash: A Programmable Completion Example

1 
1 8.8 A Programmable Completion Example
1 =====================================
1 
1 The most common way to obtain additional completion functionality beyond
1 the default actions 'complete' and 'compgen' provide is to use a shell
1 function and bind it to a particular command using 'complete -F'.
1 
1    The following function provides completions for the 'cd' builtin.  It
1 is a reasonably good example of what shell functions must do when used
1 for completion.  This function uses the word passsed as '$2' to
1 determine the directory name to complete.  You can also use the
1 'COMP_WORDS' array variable; the current word is indexed by the
1 'COMP_CWORD' variable.
1 
1    The function relies on the 'complete' and 'compgen' builtins to do
1 much of the work, adding only the things that the Bash 'cd' does beyond
11 accepting basic directory names: tilde expansion (⇒Tilde
 Expansion), searching directories in $CDPATH, which is described above
1 (⇒Bourne Shell Builtins), and basic support for the 'cdable_vars'
1 shell option (⇒The Shopt Builtin).  '_comp_cd' modifies the value
1 of IFS so that it contains only a newline to accommodate file names
1 containing spaces and tabs - 'compgen' prints the possible completions
1 it generates one per line.
1 
1    Possible completions go into the COMPREPLY array variable, one
1 completion per array element.  The programmable completion system
1 retrieves the completions from there when the function returns.
1 
1      # A completion function for the cd builtin
1      # based on the cd completion function from the bash_completion package
1      _comp_cd()
1      {
1          local IFS=$' \t\n'    # normalize IFS
1          local cur _skipdot _cdpath
1          local i j k
1 
1          # Tilde expansion, with side effect of expanding tilde to full pathname
1          case "$2" in
1          \~*)    eval cur="$2" ;;
1          *)      cur=$2 ;;
1          esac
1 
1          # no cdpath or absolute pathname -- straight directory completion
1          if [[ -z "${CDPATH:-}" ]] || [[ "$cur" == @(./*|../*|/*) ]]; then
1              # compgen prints paths one per line; could also use while loop
1              IFS=$'\n'
1              COMPREPLY=( $(compgen -d -- "$cur") )
1              IFS=$' \t\n'
1          # CDPATH+directories in the current directory if not in CDPATH
1          else
1              IFS=$'\n'
1              _skipdot=false
1              # preprocess CDPATH to convert null directory names to .
1              _cdpath=${CDPATH/#:/.:}
1              _cdpath=${_cdpath//::/:.:}
1              _cdpath=${_cdpath/%:/:.}
1              for i in ${_cdpath//:/$'\n'}; do
1                  if [[ $i -ef . ]]; then _skipdot=true; fi
1                  k="${#COMPREPLY[@]}"
1                  for j in $( compgen -d -- "$i/$cur" ); do
1                      COMPREPLY[k++]=${j#$i/}        # cut off directory
1                  done
1              done
1              $_skipdot || COMPREPLY+=( $(compgen -d -- "$cur") )
1              IFS=$' \t\n'
1          fi
1 
1          # variable names if appropriate shell option set and no completions
1          if shopt -q cdable_vars && [[ ${#COMPREPLY[@]} -eq 0 ]]; then
1              COMPREPLY=( $(compgen -v -- "$cur") )
1          fi
1 
1          return 0
1      }
1 
1    We install the completion function using the '-F' option to
1 'complete':
1 
1      # Tell readline to quote appropriate and append slashes to directories;
1      # use the bash default completion for other arguments
1      complete -o filenames -o nospace -o bashdefault -F _comp_cd cd
1 
1 Since we'd like Bash and Readline to take care of some of the other
1 details for us, we use several other options to tell Bash and Readline
1 what to do.  The '-o filenames' option tells Readline that the possible
1 completions should be treated as filenames, and quoted appropriately.
1 That option will also cause Readline to append a slash to filenames it
1 can determine are directories (which is why we might want to extend
1 '_comp_cd' to append a slash if we're using directories found via
1 CDPATH: Readline can't tell those completions are directories).  The '-o
1 nospace' option tells Readline to not append a space character to the
1 directory name, in case we want to append to it.  The '-o bashdefault'
1 option brings in the rest of the "Bash default" completions - possible
1 completion that Bash adds to the default Readline set.  These include
1 things like command name completion, variable completion for words
1 beginning with '{', completions containing pathname expansion patterns
1 (⇒Filename Expansion), and so on.
1 
1    Once installed using 'complete', '_comp_cd' will be called every time
1 we attempt word completion for a 'cd' command.
1 
1    Many more examples - an extensive collection of completions for most
1 of the common GNU, Unix, and Linux commands - are available as part of
1 the bash_completion project.  This is installed by default on many
1 GNU/Linux distributions.  Originally written by Ian Macdonald, the
1 project now lives at <http://bash-completion.alioth.debian.org/>.  There
1 are ports for other systems such as Solaris and Mac OS X.
1 
1    An older version of the bash_completion package is distributed with
1 bash in the 'examples/complete' subdirectory.
1