Skip to content

Instantly share code, notes, and snippets.

@aliang
Created June 14, 2011 07:14
Show Gist options
  • Select an option

  • Save aliang/1024466 to your computer and use it in GitHub Desktop.

Select an option

Save aliang/1024466 to your computer and use it in GitHub Desktop.
Add auto complete to your ssh, put into your .bash_profile
_complete_ssh_hosts ()
{
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
comp_ssh_hosts=`cat ~/.ssh/known_hosts | \
cut -f 1 -d ' ' | \
sed -e s/,.*//g | \
grep -v ^# | \
uniq | \
grep -v "\[" ;
cat ~/.ssh/config | \
grep "^Host " | \
awk '{print $2}'
`
COMPREPLY=( $(compgen -W "${comp_ssh_hosts}" -- $cur))
return 0
}
complete -F _complete_ssh_hosts ssh
@crmpicco

crmpicco commented Oct 8, 2018

Copy link
Copy Markdown

I came back for a reminder when setting up a new machine 👍

@xv1t

xv1t commented Oct 17, 2018

Copy link
Copy Markdown

Thanx from my MacBook :) 👍

@dinopickaxe

Copy link
Copy Markdown

Hello,
I did not have a ~/.ssh/config file so I got an error. So I modified the script a little to redirect errors from 'cat'.

_complete_ssh_hosts ()
{
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    comp_ssh_hosts=`cat ~/.ssh/known_hosts 2>/dev/null | \
                    cut -f 1 -d ' ' | \
                    sed -e s/,.*//g | \
                    grep -v ^# | \
                    uniq | \
                    grep -v "\[" ;
                    cat ~/.ssh/config 2>/dev/null | \
                    grep "^Host " | \
                    awk '{print $2}'
                    `
    COMPREPLY=( $(compgen -W "${comp_ssh_hosts}" -- $cur))
    return 0
}
complete -F _complete_ssh_hosts ssh

@r-souza

r-souza commented Dec 15, 2020

Copy link
Copy Markdown

Great!

@mherkazandjian

Copy link
Copy Markdown

i was getting some color fomatting chars in the output off ssh config. It turns out it was due to the colors emitted by "grep".
Probably to make this more robust the --color=never flag can be added to grep

_complete_ssh_hosts ()
{
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        comp_ssh_hosts=`cat ~/.ssh/known_hosts | \
                        cut -f 1 -d ' ' | \
                         sed -e s/,.*//g | \
                         grep -v ^# | \
                         uniq | \
                         grep -v "\[" ;
                         cat ~/.ssh/config | \
                         grep --color=never "^Host " | \
                         awk '{print $2}'
                   `
        COMPREPLY=( $(compgen -W "${comp_ssh_hosts}" -- $cur))
        return 0
}
complete -F _complete_ssh_hosts ssh

@cossio

cossio commented Sep 24, 2022

Copy link
Copy Markdown

It doesn't work for me, I get ~/.zprofile: command not found: complete

@baonq-me

baonq-me commented Dec 6, 2022

Copy link
Copy Markdown

It doesn't work for me, I get ~/.zprofile: command not found: complete

The same. MacOS Monterey 12.5.1

@Lat31320

Lat31320 commented Mar 2, 2023

Copy link
Copy Markdown

Thanks for this script.

I removed the known_hosts section because I want only the .ssh/config file to be parsed.

_complete_ssh_hosts ()
{
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        comp_ssh_hosts=`cat ~/.ssh/config | \
                         grep --color=never "^Host " | \
                         awk '{print $2}'
                   `
        COMPREPLY=( $(compgen -W "${comp_ssh_hosts}" -- $cur))
        return 0
}
complete -F _complete_ssh_hosts ssh

@timhughes

timhughes commented Aug 1, 2023

Copy link
Copy Markdown

It doesn't work for me, I get ~/.zprofile: command not found: complete

The same. MacOS Monterey 12.5.1

The script is written in bash. zsh has a different way of doing things.

Converted the searches to AWK so there is only 2 commands being called instead of 9 and it is easy to comment out one of the sources if you don't like it.

_complete_ssh_hosts ()
{
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        comp_ssh_hosts=`
            awk '{split($1,aliases,","); if (aliases[1] !~ /^\[/) print aliases[1]}' ~/.ssh/known_hosts ;
            awk '/^Host/ && $2 !~ /[*]/ {print $2}' ~/.ssh/config ;
            `
        COMPREPLY=( $(compgen -W "${comp_ssh_hosts}" -- $cur))
        return 0
}
complete -F _complete_ssh_hosts ssh

@michaelleb

Copy link
Copy Markdown

Actually, this can work for zsh as well, we just need to enable bash style autocomplete before using this function.
Here is complete example: put this inside ~/.zshrc

#### initialize completion system ####
# see https://unix.stackexchange.com/questions/593433/bash-like-autocompletion-for-ssh-command-in-zsh-shell-with-etc-hosts-file
# see https://zsh.sourceforge.io/Doc/Release/Completion-System.html
autoload -Uz compinit; compinit


# enable bash style autocomplete (requires compinit to be called before this)
# see https://stackoverflow.com/questions/3249432/can-a-bash-tab-completion-script-be-used-in-zsh
# see https://zsh.sourceforge.io/Doc/Release/Completion-System.html
autoload -Uz bashcompinit; bashcompinit


# replace the default ssh autocomplete that comes from compinit 
# with this _complete_ssh_hosts function which looks for hosts in ~/.ssh/config and ~/.ssh/known_hosts
# Note: requires bashcompinit to enable this bash style autocomplete function
# see https://stackoverflow.com/questions/52438964/mac-autocomplete-for-ssh-hosts-in-terminal
# see https://gist.github.com/aliang/1024466
_complete_ssh_hosts ()
{
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        comp_ssh_hosts=`
            awk '{split($1,aliases,","); if (aliases[1] !~ /^\[/) print aliases[1]}' ~/.ssh/known_hosts ;
            awk '/^Host/ && $2 !~ /[*]/ {print $2}' ~/.ssh/config ;
            `
        COMPREPLY=( $(compgen -W "${comp_ssh_hosts}" -- $cur))
        return 0
}
complete -F _complete_ssh_hosts ssh


@kserradell

Copy link
Copy Markdown

Thanks!!!

@robinbowes

Copy link
Copy Markdown

I tweaked it to work when you have multiple known_hosts files, and multiple config files:

# shellcheck shell=bash

# Add tab completion for SSH hostnames
#
# Based on this gist: https://gist.github.com/aliang/1024466
#
# Search in:
#  - ~/.ssh/config
#  - ~/.ssh/config.d/*config
#  - ~/.ssh/*known_hosts

_complete_ssh_hosts ()
{
  COMPREPLY=()
  cur="${COMP_WORDS[COMP_CWORD]}"
  comp_ssh_hosts=$(
    sort -u < <(
      awk '{split($1,aliases,","); if (aliases[1] !~ /^\[/) print aliases[1]}' < <(
        # list all known_hosts files, ignoring blank lines and comments
        # awk code taken from https://stackoverflow.com/a/17396799
        awk '!/^ *#/ && NF' < <(cat ~/.ssh/*known_hosts)
      )
      awk '/^Host/ && $2 !~ /[*]/ {print $2}' <(
        cat ~/.ssh/{config,config.d/*config}
      )
    )
  )
  COMPREPLY=( $(compgen -W "$comp_ssh_hosts" -- $cur) )
  return 0
}
complete -F _complete_ssh_hosts ssh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment