I'm using SaltStack. I would like to auto-complete the minion name when calling the salt command.

The following line has been added into ~/.bashrc:

complete -o default -o nospace -W "$(sudo ls -1 /var/cache/salt/master/minions)" salt

Then typing salt inTabsalt integration-Tab; I can see it works as expected:

$ salt integration-TabTab
integration-c   integration-u   integration-u2

To use with sudo, I have added complete -cf sudo into ~/.bashrc, but it didn't work:

    sudo salt inTab

returned nothing.

I also have tried to install bash_completion and added the following lines to ~/.bash_profile:

if [ -f $(brew --prefix)/etc/bash_completion ]; then
    . $(brew --prefix)/etc/bash_completion

but no luck.

Did I miss something?


Oh, the first thing I would like to say is sometimes it works:

$ sudo salt integration-TabTab
integration-c   integration-u   integration-u2

and sometimes it doesn't.

So first, let's see how much your bash_completion package does.

How can I check that? Here's my function:

# a wrapper method for the next one, when the offset is unknown
    local offset i

    # find actual offset, as position of the first non-option
    for (( i=1; i <= COMP_CWORD; i++ )); do
        if [[ "${COMP_WORDS[i]}" != -* ]]; then
    _command_offset $offset

# A meta-command completion function for commands like sudo(8), which need to
# first complete on a command, then complete according to that command's own
# completion definition - currently not quite foolproof (e.g. mount and umount
# don't work properly), but still quite useful.
    local cur func cline cspec noglob cmd i char_offset word_offset \


    # rewrite current completion context before invoking
    # actual command completion

    # find new first word position, then
    # rewrite COMP_LINE and adjust COMP_POINT
    local first_word=${COMP_WORDS[$word_offset]}
    for (( i=0; i <= ${#COMP_LINE}; i++ )); do
        if [[ "${COMP_LINE:$i:${#first_word}}" == "$first_word" ]]; then
    COMP_POINT=$(( COMP_POINT - $char_offset ))

    # shift COMP_WORDS elements and adjust COMP_CWORD
    for (( i=0; i <= COMP_CWORD - $word_offset; i++ )); do
    for (( i; i <= COMP_CWORD; i++ )); do
        unset COMP_WORDS[i];
    COMP_CWORD=$(( $COMP_CWORD - $word_offset ))

    _get_comp_words_by_ref cur

    if [[ $COMP_CWORD -eq 0 ]]; then
        COMPREPLY=( $( compgen -c -- "$cur" ) )
        if complete -p ${cmd##*/} &>/dev/null; then
            cspec=$( complete -p ${cmd##*/} )
            if [ "${cspec#* -F }" != "$cspec" ]; then
                # complete -F <function>

                # get function name
                func=${cspec#*-F }
                func=${func%% *}

                if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
                    $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}" "${COMP_WORDS[${#COMP_WORDS[@]}-2]}"
                    $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}"

                # remove any \: generated by a command that doesn't
                # default to filenames or dirnames (e.g. sudo chown)
                # FIXME: I'm pretty sure this does not work!
                if [ "${cspec#*-o }" != "$cspec" ]; then
                    cspec=${cspec#*-o }
                    cspec=${cspec%% *}
                    if [[ "$cspec" != @(dir|file)names ]]; then
            elif [ -n "$cspec" ]; then
                COMPREPLY=( $( eval compgen "$cspec" -- "$cur" ) );
        elif [ ${#COMPREPLY[@]} -eq 0 ]; then

if you type sudo mkdirTabTab, does it show a list of directories?


$ sudo mkdir TabTab
.FontForge/        .djangopypi2/      .ievms/            .ssh/              .wireshark-etc/
  • 1
    Chaining together command completions usually involves completion functions that invoke a function defined in the completion package called _command_offset. So first, let's see how much your bash_completion package does. If you remove the complete -cf sudo line from .bashrc, then logout and login again, and just use the support built in to bash_completion, do you see any special handling at all for sudo, e.g., if you type sudo mkdir <tab><tab>, does it show a list of directories? – Mark Plotnick Jul 11 '14 at 14:38
  • @MarkPlotnick: if you type sudo mkdir <tab><tab>, does it show a list of directories? --> Yes, see my update in the original question. – quanta Jul 12 '14 at 4:49
  • 1
    If completion chaining is sometimes working and sometimes not, the first thing I'd check is the completion definition for sudo when completions are not working. Do this by typing complete|grep sudo. Bash on OSX reads its init files in nonintuitive ways (see apple.stackexchange.com/a/13019), so my hunch is that sometimes not all your completion definitions are being read in or they are being superseded by unwanted definitions. – Mark Plotnick Jul 14 '14 at 21:29

Check set -o to see if maybe posix mode is enabled. If so, disable with set +o posix

In "posix" mode, bash's tab-completion is for some reason disabled in "vi-mode". I've not seen any explanation why this behavior is so, nor why it's specific to vi-mode, so I didn't bother explaining.

PS: This answer is more suited for someone who stumbles upon the question via a search engine... not for your particular setup.

Chet Ramey says:

in POSIX mode, the vi editing mode should not map tab to complete. This is because POSIX.2 completely specifies the behavior of the vi editing mode, and the standard requires tab to be mapped to self insert by default.


Quick answer:

  • Install bash-completion
  • Source bash_completion on Bash startup
  • Add your compspec on Bash startup
  • Don't overwrite sudo compspec with complete -cf sudo

I suppose that you use MacOSX with brew.


brew update
brew install bash-completion
brew info bash-completion
# bash-completion: stable 1.3
. $(brew --prefix)/etc/bash_completion
complete -p sudo

You should see something like this:

complete -F _root_command sudo


function _comp_foo() { COMPREPLY=($(compgen -W 'a b c' -- "$2")); }
complete -F _comp_foo foo

Type foo SpaceTabTab
You should see a b c

Type sudo foo SpaceTabTab
You should see a b c

Then remove complete -fc sudo from your initialisation files (~/.bash_profile, ~/.bashrc etc)
Add the following lines to your initialisation files:

if [ -f $(brew --prefix)/etc/bash_completion ]; then
  . $(brew --prefix)/etc/bash_completion

# Your compspec here
complete -o default -o nospace -W "$(sudo ls -1 /var/cache/salt/master/minions)" salt

Reopen terminal.
Type complete -p sudo.
You should see complete -F _root_command sudo.

Type complete -p salt.
You should see something like this:

complete -o default -o nospace -W 'a
c' salt

bash-completion2.* note:
You can install bash-completion2: https://github.com/Homebrew/homebrew/issues/19258

  • 2.* works with Bash 4.*
  • 2.* uses the -D option for compspecs lazy loading. So, complete -p sudo outputs complete: sudo: no completion specification until you type sudoSpaceTab

sudo doesn't give you an login shell unless you ask for it with -i. So you'll find that without it you'll need to load the system's completions:

. /etc/bash_completion

If you run with -i, then $HOME will be ~root, and so anything written to the user's ~/.bashrc won't get read. If you can separate out the completion stuff from the rest of your ~/.bashrc, source that with . and that should work.

  • But this question is about somebody typing a sudo command into his default shell.  sudo hasn't started running yet, so its behavior is irrelevant; this is all about the behavior of the user's shell. – G-Man Says 'Reinstate Monica' Jun 27 '15 at 7:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.