Autocomplete server names for SSH and SCP

Found it!!

It seems that in Ubuntu the entries in ~/.ssh/known_hosts are hashed, so SSH completion cannot read them. This is a feature, not a bug. Even by adding HashKnownHosts no to ~/.ssh/config and /etc/ssh/ssh_config I was unable to prevent the host hashing.

However, the hosts that I am interested in are also found in ~/.ssh/config. Here is a script for Bash Completion that reads the entries from that file:

_ssh() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(grep '^Host' ~/.ssh/config ~/.ssh/config.d/* 2>/dev/null | grep -v '[?*]' | cut -d ' ' -f 2-)

    COMPREPLY=( $(compgen -W "$opts" -- ${cur}) )
    return 0
}
complete -F _ssh ssh

Put that script in /etc/bash_completion.d/ssh and then source it with the following command:

$ . /etc/bash_completion.d/ssh

I found this guide invaluable and I would not have been able to script this without it. Thank you Steve Kemp for writing that terrific guide!


Pre-packaged

You don't say what distro you're using but on my Fedora 19 system I have the following package installed, bash-completion which provides this feature through this completion rule file:

/usr/share/bash-completion/completions/ssh

Here's the package I have installed:

$ rpm -aq |grep completion
bash-completion-2.1-2.fc19.noarch

If you look through that rule file you'll see stanzas that are interrogating the $HOME/.ssh/config file:

$ grep config /usr/share/bash-completion/completions/ssh
    local configfile
    local -a config
        # Search COMP_WORDS for '-F configfile' or '-Fconfigfile' argument
                    configfile="$(dequote "${1:2}")"
                    [[ $1 ]] && configfile="$(dequote "$1")"
        _known_hosts_real -a -F "$configfile" "$cur"
    local configfile
        # Search COMP_WORDS for '-F configfile' argument
                    configfile="$(dequote "${1:2}")"
                    [[ $1 ]] && configfile="$(dequote "$1")"
        _known_hosts_real -a -F "$configfile" "$cur"
    local configfile prefix
        # Search COMP_WORDS for '-F configfile' or '-Fconfigfile' argument
                    configfile="$(dequote "${1:2}")"
                    [[ $1 ]] && configfile="$(dequote "$1")"
                _known_hosts_real -c -a -F "$configfile" "$cur"

Rolling your own

I also found this Gist, known_hosts_autocomplete.sh, that does something similar except with the $HOME/.ssh/known_hosts file.

# add to ~/.bash_profile, and close/reopen a shell.  Will autocomplete any
# hosts found in known_hosts.

complete -W "$(echo `cat ~/.ssh/known_hosts | cut -f 1 -d ' ' | \
    sed -e s/,.*//g | uniq | grep -v "\["`;)" ssh

You could do something similar using your $HOME/.ssh/config file if for some reason you're unable to find the completion rule file for ssh already pre-packaged.


I found that the autocomplete was not working because Ubuntu hashes known hosts. You can add

Host *
    HashKnownHosts no

To your .ssh/config file but existing hosts won't be un-hashed.