/ root / pages / bashcompletion.html

You're using an old link! - Thankfully, you no longer need to specify a nonstandard port (8080) to access my site. You could've used the more standard: http://pbrisbin.com/pages/bashcompletion.html.

Bash Completion


Almost all shells have some form of tab completion: ls -l ./Doc<tab> and it completes to ./Documents. Not only is this a convenience, but it can really help with long or similarly named arguments to prevent human error.

I hear that zsh has the best terminal completions, but I can't drag myself away from bash (no matter how enticing those suffix aliases sound).

Anyway, extensions to the normal filename completions in bash can be found in Arch's bash-completion package. It installs functions in /etc/bash_completion.d/ for various commands to complete. Turns out, making custom completion functions isn't all that hard.

My Script

I added my first custom bash completion for my bookmark script. The goal was, if I type bookmark -a li<tab> bash will use the folder structure under ~/.my_bookmarks to complete the category argument. That function can be found here for reference.

I'm planning on doing more of these, because (once I found a good template) it was extremely easy to create, and immediately stopped me from having to bookmark -ls to get a list of valid categories before doing bookmark -a foo/bar url to add it without an error.

Here's a commented example to try and explain how it all works; maybe it can be useful to others

Our Script

Our script is a killer burning application which accepts .isos for input and burns them to CD or DVD. It burns with cdrecord when passed -c something.iso and growisofs when passed -d something.iso.

We want tab to only complete ISO files in the current directory. But, we're wicked organized so we actually have a shweet naming convention where-by the filenames start with cd_ or dvd_ so we know what will fit on what.

#
# /etc/bash_completion.d/killerburn
#

_killerburn_completion()
{

  #
  # it's best to declare all variables locally
  # so that you don't pollute your environment
  #
  # you can give them values here or just list
  # them on one line (or mixed)
  #
  local cd_comps dvd_comps cur prev word


  # the contents of this array are the values
  # that bash will use as possible completions
  #
  # it can be an explicit list of values or the
  # output of a command

  # the list we want to complete on -c
  cd_comps=( $(find ./ -name 'cd_*.iso') )

  # and a more different list for -d
  dvd_comps=( $(find ./ -name 'dvd_*.iso') )


  # declare the array that will hold your 
  # responses to tab-completion attempts
  COMPREPLY=()


  # get the current word (characters so far)
  cur=${COMP_WORDS[COMP_CWORD]}

  # and the most recent word (preceding flag)
  prev=${COMP_WORDS[COMP_CWORD-1]}

  # you could even go back further if needed


  # since we know $prev we can do some useful
  # logic, you could even use $cur if needed

  # the comps for -c
  if [ "$prev" == '-c' ]; then

    COMPREPLY=( $(compgen -W "\
                $(  for word in ${cd_comps[@]}; do # these are probably the only
                    echo "$word"                   # spots you'll ever edit
                    done )" -- $cur ) )

  # and the comps for -d
  elif [ "$prev" == '-d' ]; then
    COMPREPLY=( $(compgen -W "\
                $(  for word in ${dvd_comps[@]}; do
                    echo "$word"
                    done )" -- $cur ) )
  fi
}

# now just enable it
complete -F _killerburn_complete killerburn

Re-source and try it

Comments

on Mon, 26 Jul 2010 00:55:36 -0400, supulton wrote:

Very nice. I've been meaning to do this.





pbrisbin dot com 2010