bash completion
Intro
A lot of time I spend in linux comand line. Sometimes I write small scripts which help me in my regular work. Often the scripts use set of the commands and parameters. I do not remember them all. I’ve to look at the source to refresh it in my memory and every time I wish I had completion for my script.
Now I know how! This article is not comprehanive tutorial. It just allows you to start work with bash completion and unfolds two ways how you can use it.
As a case study let’s use the following simple program.
Usage:
testwar.sh <commnad>
Example:
testwar.sh download # Downloads latest build from CI
testwar.sh deploy # Deploys test.war and starts server
testwar.sh start # Starts test server
testwar.sh stop # Stops test server
And I want to use bash completion for this program.
Completion info in the same file
Look at the following script. It contains both
- bash completion function which is part of the same script
- emulation of working code (real functions’ body replaced with echo ‘…’)
Completion in the same script file | download: testwar.sh |
---|
1 #!/bin/bash
2
3 function download(){ echo "Downloads war from CI" ; }
4
5 function deploy(){ echo "Deploys war file" ; }
6
7 function start(){ echo "Starts server" ; }
8
9 function stop(){ echo "Stops server" ; }
10
11 get_functions(){
12 cat $0 | sed -n /^function/p | sed -e "s|function *\([^(]*\).*|\1|"
13 }
14
15 show_completion(){
16 # Get current incomplete completion word
17 local cur=$(echo "$COMP_LINE" | sed -e "s|$0 *.* \([^ ]*\)|\1|")
18 # Get number of spaces
19 let num=$(echo "$COMP_LINE" | sed -e "s|[^ ]||g" | wc -m)
20
21 # Stop show completion if more than 2 words in $COMP_LINE
22 # first word is prog name ($0)
23 # second word is completed command followed by space
24 if [ $num -lt 3 ] ; then
25 compgen -W '`get_functions` FROM_SAME_FILE' -- $cur
26 fi
27 }
28
29 run_command(){
30 echo "running command '$1'"
31 for i in `get_functions` ; do
32 if [ "$i" == "$1" ] ; then
33 eval $1
34 return 0
35 fi
36 done
37 echo "Error: unknown command '$1'"
38 return 1
39 }
40
41 do_work(){ [ $# -eq 1 ] && run_command $1 || usage ; }
42
43 usage(){ echo "Print usage info..." ; }
44
45 # Entry point
46 [ -n "${SHOW_COMPLETION}" ] && show_completion $@ || do_work $@
The main interest is the function show_completion()
which do all the completion job. This function
is activated when SHOW_COMPLETION
environment variable is set and not empty. You can see last
line in code which switches between returning completion info and normal program flow.
The script uses functions which declared with keyword function
as commands. So, method
get_functions
returns all known commands. This information is used both in auto-completion and
in script as well to check whether the command provided by user is known one. When you type program
name and then press TAB you see
>./testwar.sh
deploy download FROM_SAME_FILE start stop
As you can see a const FROM_SAME_FILE is added to the list of the completion words. It’s done to distinguish this example from the second one where bash completion is done with help of the shell function, not the command.
To add completion to the current shell session use the command:
complete -C 'SHOW_COMPLETION=true ./testwar.sh' testwar.sh
Completion info in individual file
The following code example behaves similar to previous one. The two major differences are
- completion info is in separate file
- it’s more common way when
COMPREPLY
andCOMP_WORDS
shell variables are used.
Individual completion file | download: testwar-completion.bash |
---|
1 get_functions(){
2 cat testwar.sh | sed -n /^function/p | sed -e "s|function *\([^(]*\).*|\1|"
3 }
4
5 function _test_war_completion(){
6 # Get current incomplete completion word
7 local cur=${COMP_WORDS[COMP_CWORD]}
8 # Show completion if one or two words in command line
9 if [ ${#COMP_WORDS[*]} -lt 3 ] ; then
10 COMPREPLY=( $(compgen -W '`get_functions` FROM_FUNCTION' -- ${COMP_WORDS[COMP_CWORD]}) )
11 fi
12 return 0
13 }
14
15 complete -F _test_war_completion testwar.sh
To add completion to the current shell session:
source testwar-completion.bash
Commom hints
To list all completion bindings
complete -p
System-wide completion
Of course, you can place testwar.sh into the dir listed in your system $PATH. In such a case you
can add complete -C 'SHOW_COMPLETION=true testwar.sh' testwar.sh
or source testwar-completion.bash
in your ~/.bashrc
file.
Filenames completion
If, for instance, you need to add filename after the command you can extend completion by predefined
options. To add default auto-completion do
complete -C 'SHOW_COMPLETION=true ./testwar.sh' -o default testwar.sh
Exit and return
Do not use exit
command in completion function. It’ll exit from current shell - I don’t think it’s
what you want when pressing TAB.
Use return $some-exit-code
instead.
Man
Use man bash
to learn about complete
and compgen
builtin functions.
Links
- Completion with function
- http://stackoverflow.com/questions/10942919/customize-tab-completion-in-shell
I guess it’s enough to start to play with auto-completion.