91

I have a few servers configured in ~/.ssh/config, such as alpha and beta. How might I configure Bash such that the commands $ ssh alTab and $ scp file.tgz alTab autocomplete the names of the configured servers?

I don't want to add the servers to another file (i.e. a Bash array) each time one is added, as we add and remove servers regularly and the list is quite large.

This is on Kubuntu 12.10, and I do have bash-completion installed.

Jon
  • 165
  • 1
  • 8
dotancohen
  • 15,864

11 Answers11

100

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 (Archive.org copy) invaluable and I would not have been able to script this without it. Thank you Steve Kemp for writing that terrific guide!

dotancohen
  • 15,864
  • 1
    It seems that in Centos 6 entries in ~/.ssh/known_hosts over-ride entries in ~/.ssh/config eg. if you have foo.bar.baz.com in known_hosts then it will match the whole thing if you do "ssh foo" - this is undesirable if you have a different user or other settings you want applied from the ssh confg. If you have an entry in ssh config that does not autocomplete properly then check if it is present in known_hosts and delete it. It should now work as normal taking the settings from config. – Imran-UK Apr 18 '16 at 14:46
  • @Imran-UK: It is known that Ubuntu does not do that sensible thing, under the pretense of security. Apparently the thought is that a malicious entity might, as the user, edit ~/.ssh/config, despite chmod 0600 on the directory. Hey, I wasn't the one who made the decision! – dotancohen Apr 18 '16 at 14:57
  • 1
    I use zsh & oh-my-zsh, I just discovered that enabling the "ssh" plugin and putting "HashKnownHosts yes" in my ~/.ssh/config seems to do the right thing. You can change shell whatever the distro so this might be a way around it. – Imran-UK Apr 20 '16 at 09:21
  • @Imran-UK: In fact I've tried zsh in the past and I do intend on trying it again. I'll look into that. Shukran my friend! – dotancohen Apr 20 '16 at 09:24
  • 1
    I know I'm a little late to the party, but RHEL 7 will autocomplete entries that are in /etc/hosts, other distros may do the same. – Centimane Jul 25 '16 at 20:23
  • Make sure the lines with "Host" do not start with a blank space, otherwise grep '^Host' ~/.ssh/config will miss them. – galath Dec 05 '16 at 10:57
  • Change your grep to a grep -R ~/.ssh/config.d and it will work for those of us who use the Include directive to have multiple config files in something like a ~/.ssh/config.d directory. – Kirk Sep 13 '17 at 16:32
  • @Kirk: It seems that will-Farrell has edited the answer to add that directory in manually. If that's not working then you are invited to edit with what does work! Thank you! – dotancohen Sep 13 '17 at 17:35
  • @dotancohen you are correct! I copied the answer from here which must have the older unedited version. – Kirk Sep 13 '17 at 19:36
  • Amazing work. Works perfectly on Ubuntu 16.04 and Debian 9. Thanks! – xZero Jun 02 '18 at 16:35
  • Note that if you have entries in your .ssh/config with multiple host entires (i.e. shared settings), this will not handle those well. – GrandOpener Jan 15 '19 at 21:59
  • I would add sort after cut – Ouss Mar 10 '21 at 17:21
  • thanks! its just what i needed for my custom sshfs script – Fuseteam May 11 '21 at 12:55
  • It's also a convenient way to put it in .bash_profile ref: https://gist.github.com/aliang/1024466 – Nick Tsai Jun 02 '22 at 06:52
  • Although this does work for tab-completing hosts, it breaks all other completions with ssh, such as ssh -i ~/.ssh/<tab>. – dimo414 Jul 24 '23 at 05:10
24

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.

slm
  • 369,824
  • Thank you. It seems that Ubuntu does not ship with /usr/share/bash-completion/completions/ssh. Could you post that entire file somewhere that I might copy it? Thanks! – dotancohen Jun 10 '14 at 07:48
  • You can get all the files from this archive: http://pkgs.fedoraproject.org/repo/pkgs/bash-completion/bash-completion-2.1.tar.bz2/4e2a9f11a4042a38ee79ddcd048e8b9e/bash-completion-2.1.tar.bz2 – slm Jun 10 '14 at 07:55
  • I'm sorry to be a pain, but even copying the ssh file from that tar archive to /usr/share/bash-completion/completions/ssh doesn't help! Neither does running the complete -W ... command from the Bash prompt. What might be the issue here? – dotancohen Jun 10 '14 at 08:10
  • Thank you. This does seem to be a known design decision in Ubuntu. Thus I am accepting the answer even though I cannot get bash completion working for ssh or scp. – dotancohen Jun 10 '14 at 09:13
  • 3
    Homebrew for OSX also has this: bash-completion and zsh-completions – Adam Nelson Oct 16 '14 at 10:36
  • On Macos I had to bump to bash-completion@2 to make this work, e.g. brew uninstall bash-completion && brew install bash-completion@2. – antonagestam Nov 18 '21 at 16:43
13

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.

M1ke
  • 579
  • Thank you. I've actually already added this directive to ~/.ssh/config and changed the directive in /etc/ssh/ssh_config, and I've removed the ~/.ssh/known_hosts file. After logging into the servers again, the host names are still being saved hashed. – dotancohen Jan 28 '15 at 15:22
  • I have edited my answer; you will need to specify a host and nest the hash directive inside; I didn't list that as many .ssh/config files will already have this line. Let me know if that works. – M1ke Jan 29 '15 at 10:28
  • This is definitely the easy way to do it! Given that (newer versions of) Ubuntu now have ssh completion enabled, this is probably what is wanted. – John Montgomery Sep 24 '15 at 08:51
  • As I added below, you can do this, but you probably shouldn't -- you don't need to in order to get completion working anyway. However, if you don't agree and just wanted to un-hash the existing hosts after adding that above, you can just rm ~/.ssh/known_hosts, and that will cause it to be re-generated (un-hashed) whenever you log into a new server. – fatal_error Jan 08 '18 at 00:31
11

To enable ssh autocompletion in Debian and Ubuntu:

sudo apt-get install bash-completion

Note that this has nothing at all to do with known_hosts hashing, contrary to what was stated above and the original question. If you wanted to autocomplete from known_hosts, then of course you would have to disable hashing, but that is strongly recommended against.

For example, I have:

Host *
    HashKnownHosts yes

in my .ssh/config, and I still have ssh auto-completion working just fine against hosts listed in .ssh/config and /etc/hosts. You do need to add the host to .ssh/config as the OP stated:

Host my-awesome-host Hostname the.real.host.name

(Or, you can add a host entry to /etc/hosts, which is another source for the Debian/Ubuntu scripts.)

Then, you can just type ssh my-awe<tab> and it will be automagically completed. Again, this is even if you HashKnownHosts, which is highly recommended. (Note that bash completion needs to be enabled in your bash shell, and you need to specifically have those scripts installed as above for your distribution.)

Then, add these lines to your .bashrc to enable it (requires a logout and log back in, or just a new bash typed in to launch a new shell. (You don't need to enable if it's already enabled in /etc/bash.bashrc and /etc/profile sources /etc/bash.bashrc).

if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi

This will enable ssh autocompletion (among other things!) from ~/.ssh/config, /etc/hosts, etc.

Note that Debian defaults to ash instead of bash. You can switch to bash easily:

sudo usermod -s /bin/bash "$USER"

(You'll need to log out and log back in for this to take effect.)

5

On Ubuntu 14.04, ssh auto-completes servers mentioned in your ~/.ssh/config

I noticed it when I realized that only one of the servers I commonly access auto-completed. The only difference between the two was an entry in the ssh config file that was related to authentication. When I added a new entry to the config file for the other server, it started auto-completing too.

Here's the entry for those who were asking:

HOST server-name 
    GSSAPIAuthentication=no

I would be very surprised if it mattered what you were specifying in the config (as long as it is still valid, of course).

Noah
  • 151
  • Thank you Noah, what was that entry? On my 14.04 and 14.10 systems autocomplete does not work. – dotancohen Apr 03 '15 at 19:52
  • I don't think the entry matters--I am pretty sure it is just having them in there--but I will add the entry to my answer. – Noah Apr 03 '15 at 20:00
1

On CentOS 7 all it took was yum install bash-completion to auto complete hosts from .ssh/config when typing ssh and hitting Tab.

Stephen Kitt
  • 434,908
Liz K
  • 11
1

On macosx brew install bash-completion then add the following to ~/.profile

if [ -f $(brew --prefix)/etc/bash_completion ]; then
  . $(brew --prefix)/etc/bash_completion
fi
Ouss
  • 121
1

Add the following into ~/.bashrc or ~/.bash_profile

# Autocomplete ssh commands
WL="$(perl -ne 'print "$1\n" if /^Host (.+)$/' ~/.ssh/config | grep -v "*" | tr "\n" " ")"
complete -o plusdirs -f -W "$WL" ssh scp

Then run source ~/.bashrc or source ~/.bash_profile to pick up the configuration

Credit > https://blog.tinned-software.net/use-ssh-comfortably-with-command-history-and-auto-complete/

j3ffyang
  • 151
1

Solution for macOS:

  1. Add the following line in ~/.bash_profile:
    complete -W "$(echo `cat ~/.ssh/config | grep "^Host " |awk '{print $2}'`;)" ssh
    
  2. Source your .bash_profile or relogin

Neither brew nor bash-complete is required. complete is a Bash internal command.

AdminBee
  • 22,803
OctavianX
  • 21
  • 2
  • For zsh you need also to enable compatibility mode. Place next code in the top of .zshrc file
    autoload -U +X compinit && compinit
    

    Then source it. Afterward add code from the answer and source .zprofile. More discussion is here https://github.com/eddiezane/lunchy/issues/57

    – Vlad Moyseenko Jul 07 '23 at 12:19
  • Remember that for several releases now MacOS has not been using bash but zsh, so bash completions will be ignored. Bash can be installed from brew, or a zsh equivalent used. – rivimey Nov 15 '23 at 17:54
0

A simple hack that worked for me was to add the hostnames to ~/.ssh/known_hosts - just one hostname per line, without any host key, i.e.

# My list of hostnames for auto-completion
host1.example.com
host2.example.com

is enough for auto-completion to work, at least on my machine.

This is useful when, for example, using SSH host certificates, so hosts are not normally added to known_hosts. Of course you have to manage this list yourself somehow.

EM0
  • 476
0

As a hacky solution for that works on Ubuntu 22.04 and probably other systems, the bash-completion library will look in ~/.ssh/known_hosts2 for hostnames, in addition to other locations. As ~/.ssh/known_hosts is in active use and full of hashed entries, you can just list your hostnames, one per line, in ~/.ssh/known_hosts2 and they'll be automatically found.

ssh does read the file, but appears content to ignore the incomplete entries, or perhaps is letting the more complete entries in ~/.ssh/known_hosts override them.