More on Using the Bash Complete Command
In the video last week I showed how to use the bash complete command for simple use cases. Today I'll show you some of the additional ways that you can use the command for more complex scenarios.
To review the simple use case I demonstrated last week here's the essence of it, or watch the video:
$ # Create a dummy command:
$ touch ~/bin/myfoo
$ chmod +x ~/bin/myfoo
$ # Create some files:
$ touch a.bar a.foo b.bar b.foo
$ # Use the command and try auto-completion.
$ # Note that all files are displayed:
$ myfoo <TAB><TAB>
a.bar a.foo b.bar b.foo
$ # Now tell bash that we only want foo files.
$ # This command tells bash args to myfoo are completed
$ # by generating a list of files and then excluding
$ # everything # that doesn't match *.foo:
$ complete -f -X '!*.foo' myfoo
$ # Tray again:
$ myfoo <TAB><TAB>
a.foo b.foo
For more complex cases where you need more control over how things are completed you can tell bash to call a function for doing the completion work. This is a function that you supply, that you would probably source from your .profile file. The function name is then supplied as an argument to the -F option of complete:
$ complete -F _mycomplete_ myfoo
A basic version of _mycomplete, which at this point doesn't do anything more than our simple command line usage of complete above, would be something like:
function _mycomplete_()
{
local cmd="${1##*/}"
local word=${COMP_WORDS[COMP_CWORD]}
local line=${COMP_LINE}
local xpat='!*.foo'
COMPREPLY=($(compgen -f -X "$xpat" -- "${word}"))
}
complete -F _mycomplete_ myfoo
In the first three lines of the function body we create some useful local variables, here mainly for showing what's available since most of them aren't used in the function. The first, cmd, gets the command that's being executed, this can be used if your completion function can handle multiple commands. The second, word, gets the word that is being completed, this can be used if your completion strategy changes based on the word that's being expanded, it's also needed so that only matching values are returned. The third, line, gets the entire command line that is being completed. The fourth variable, xpat, is our exclusion pattern, the same one used in the simple example above. Check the bash man page for other useful COMP_* variables.
The only real code in the function is the last line that sets the variable COMPREPLY, which is our reply to bash's request to expand something. This line uses compgen to generate the expansion. The compgen command accepts most of the same options that complete does but it generates results rather than just storing the rules for future use. Here we tell compgen to create a list of files with -f. Then we tell it to exclude all the files that match our exclusion pattern with -X "$xpat". And finally, we pass in the word being completed so that only items that match it are returned.
Now, let's consider a slightly more complex example. Let's use our complete function to complete multiple commands and let's also change it so that it expands things for the myfoo command differently depending on the command line arguments given to myfoo.
Specifically, let's assume that myfoo can both foo things and unfoo them, so myfoo -f myfile creates myfile.foo and myfoo -u myfile.foo creates myfile. Think of the -d option to gzip or bunzip2. So, in this case we only want to show non foo files if -f has been specified, and only foo files if -u has been specified.
Further, let's add one more enhancement, let's make our completion function include directory names so that directory names can be used to complete the argument, thereby allowing us to navigate to sub-directories for fooing and unfooing files in subdirectories. Our new function would be:
function _mycomplete_()
{
local cmd="${1##*/}"
local word=${COMP_WORDS[COMP_CWORD]}
local line=${COMP_LINE}
local xpat
# Check to see what command is being executed.
case "$cmd" in
myfoo)
# See if we are fooing or unfooing.
case "$line" in
*-f*)
xpat='*.foo'
;;
*-u*)
xpat='!*.foo'
;;
*)
xpat='*.foo'
;;
esac
;;
mybar)
xpat='!*.bar'
;;
*)
xpat='!*'
;;
esac
COMPREPLY=($(compgen -f -X "$xpat" -- "${word}"))
}
complete -d -X '.[^./]*' -F _mycomplete_ myfoo mybar
Here, we use the cmd variable to see which command we are completing and change our pattern based on it. When handling the myfoo command, we use the line variable to see if the command line includes the -f or -u option for determining whether we should exclude or include foo files.
To include directories in our output we simply modify the complete command that installs our function by including the arguments -d -X '.[^./]*', which generates a list of directories and then excludes ./ and ../ (the current directory and the parent directory). The directory list is then added to the result returned by calling our completion funtion. We also add our second command mybar to the commands handled by our function.
Now when we run it:
$ # Source our completion function:
$ . bcomp2.sh
$ # Make some files:
$ touch a b a.bar a.foo b.bar b.foo
$ # Make a directory for checking directory inclusion:
$ mkdir astuff
$ # See what we get when we want to foo something
$ myfoo -f <TAB><TAB>
a a.bar astuff/ b b.bar bcomp2.sh bcomp.sh
$ # See what we get when we want to unfoo something
$ myfoo -u <TAB><TAB>
a.foo astuff/ b.foo
Once you've digested all of this, check the file /etc/profile.d/complete.bash to see the default completions that come with bash. You'll notice in that file that there are numerous complications that we've ignored here, such as what happens when somebody is trying to complete a word such as ${ABC or $(ca. In these cases the completion needs to return, respectively, a variable name and a command name and not a data file name.
Mitch Frazier is an Associate Editor for Linux Journal.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
- Dynamic DNS—an Object Lesson in Problem Solving
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Download the Free Red Hat White Paper "Using an Open Source Framework to Catch the Bad Guy"
- Validate an E-Mail Address with PHP, the Right Way
- A Topic for Discussion - Open Source Feature-Richness?
- New Products
- Dart: a New Web Programming Experience
- myip
2 hours 9 min ago - Keeping track of IP address
4 hours 53 sec ago - Roll your own dynamic dns
9 hours 14 min ago - Please correct the URL for Salt Stack's web site
12 hours 25 min ago - Android is Linux -- why no better inter-operation
14 hours 41 min ago - Connecting Android device to desktop Linux via USB
15 hours 9 min ago - Find new cell phone and tablet pc
16 hours 7 min ago - Epistle
17 hours 36 min ago - Automatically updating Guest Additions
18 hours 45 min ago - I like your topic on android
19 hours 31 min ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
Tab completion like ncftp?
Dear Mitch,
Awesome post.
I have been trying to write a tab completion that works just like in ncftp, which is:
$ ls
file1 file2 file3
$ cat file[TAB]
1 2 3
So I was wondering, if you could help me out? =)
Right now I have
_b() {
local word=${COMP_WORDS[COMP_CWORD]}
echo $word;
}
complete -F _b TEST
Is it possible to have this work for all commands, and not just TEST?
And how do I remove $word from "compgen"'s output?
Best regards,
Louise
Completion
The following should remove the "word" from the completion list. Completion functions just return a bash array and you can manipulate it to contain whatever you like:
_b() { local word=${COMP_WORDS[COMP_CWORD]} COMPREPLY=($(compgen -f -- "${word}")) if [[ "$word" ]]; then local w local i=0 local n=${#COMPREPLY[*]} while [[ $i -lt $n ]] do w=${COMPREPLY[$i]} COMPREPLY[$i]="${w:${#word}}" let i++ done fi }I don't see any way to get a completion to work for all commands but I suppose you could always do something like this if you really need to:
for c in /bin/* /usr/bin/* ~/bin/* do complete -F _b $(basename $c) doneMitch Frazier is an Associate Editor for Linux Journal.
Thank u sir, useful one.
Thank u sir, useful one.
Autocompletion
You know you're using autocompletion too much when you start hitting [TAB] to try to autocomplete your username (or worse, your password) during login...
:)
TAB TAB TAB
TAB TAB TAB -- hmmm I had a really snappy response, why won't this thing complete it..png)
Mitch Frazier is an Associate Editor for Linux Journal.