SSH Bash Completion

Today I was having a look on how the git-completition is implemented. If you have not enabled the completition for git, I think you should really read my article on how to do it.

So I found this two links, that explain the basics on how to implement bash completion for simple commands:

And I implemented my completion for ssh, which takes the hostnames from the file ~/.ssh/known_hosts

# Add bash completion for ssh: it tries to complete the host to which you 
# want to connect from the list of the ones contained in ~/.ssh/known_hosts

__ssh_known_hosts() {
    if [[ -f ~/.ssh/known_hosts ]]; then
        cut -d " " -f1 ~/.ssh/known_hosts | cut -d "," -f1
    fi
}

_ssh() {
    local cur known_hosts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    known_hosts="$(__ssh_known_hosts)"
    
    if [[ ! ${cur} == -* ]] ; then
    	COMPREPLY=( $(compgen -W "${known_hosts}" -- ${cur}) )
        return 0
    fi
}

complete -o bashdefault -o default -o nospace -F _ssh ssh 2>/dev/null \
	|| complete -o default -o nospace -F _ssh ssh

To load it, save the above script into a file called ‘ssh-completion’ then add “source ssh-completion” in your ~/.profile or ~/.bashrc. Some Linux distributions offer a directory where you can deploy your completion scripts, for example debian should have them like /etc/bash-completion.d/foobar.bash.

Then go on the command line and type:

$ ssh [tab]

If everything went fine, you should see a list of servers to which you recently connected.

I don’t go in detail to explain the above script, since there are good tutorials around for this purpose, including the links I included before.
I only want to say that the ‘compgen’ is an internal bash command, used in the above script: what it does is taking a list of words (-W ${known_hosts} option) from which to try to complete a given partial word (in the above example the ${cur} variable, which is the word at the cursor).

Since completion is usually implemented with bash scripts, it’s good to keep handy a bash reference manual.

Scared of bash scripts?

I am. I continuously forget the syntax of loops and conditional expressions: I think that the bash language is quite obscure and counter-intuitive. So I guess that is also possible to implement completion as part of your program using your language of choice (Java in my case). You can implement a feature in your program that reads and analyzes the COMP_WORDS and COMP_CWORD environment variables, and generate the list of completion words, as the ‘compgen’ bash built-in command does. And, if your language allows it, set those words into the COMPREPLY environment variable; in Java I think it’s not possible to alter environment variables, but you can just output the words on the standard output and use a brief script to set that output to the COMPREPLY variable.
I’d suggest to bind this functionality to a ‘–completion’ option that can be specified in the command line, as we usually do for the –help option.

See git-completion.bash if you want an example of a complex completion script… bash can be hard.

Update

I just typed ‘complete’ on my mac, and I discovered I have 408 commands binded to some completion script. Including a more advanced ssh script which implements the hostname suggestions.


3 Responses to “SSH Bash Completion”  

  1. 1 Fred New

    Hmm, SSH Bash completion based on known_hosts, I never thought of that. I have one based on ~/.ssh/config. (Let me know if you see any bugs.) I have more than 500 Host satements in my ~/.ssh.config.:
    _ssh()
    {
    local cur prev hosts
    COMPREPLY=()
    cur=”${COMP_WORDS[COMP_CWORD]}”
    prev=”${COMP_WORDS[COMP_CWORD-1]}”

    #
    # Complete the arguments based on Host lines in ~/.ssh/config
    # All names on the Host line are added to the lookup glossary.
    #
    if [ -f $HOME/.ssh/config ]; then
    hosts=$(grep “^Host ” $HOME/.ssh/config | awk ‘{for (i=2; i<=NF; i ) {print $i}}')
    COMPREPLY=($(compgen -W "${hosts}" — ${cur}))
    fi
    }
    # For scp, we add filename completion, the other commands just get hostname completion.
    # (Only for bash)
    if [ -n "$BASH_VERSION" ]; then
    complete -f -F _ssh scp
    complete -F _ssh host sftp ssh sshgr
    fi

    As you can see, I also use this for the host command. The sshgr script greps out stanzas from my ~/.ssh/config file that contain the host I'm curious about.

    Additionally, Red Hat, CentOS, Fedora allow you to store profile modifications in /etc/profile.d/. I store the above as ssh-bash-completion.sh there.

  2. 2 Robin Kearney

    Hi, if you change the if statement to this:

    if [[ ! ${cur} == -* ]] ; then
    if [[ ${cur} == *@* ]] ; then
    COMPREPLY=( $(compgen -W “${known_hosts}” -P ${cur/@*/}@ — ${cur/*@/}) )
    else
    COMPREPLY=( $(compgen -W “${known_hosts}” — ${cur}) )
    fi
    fi

    it works in the case of ssh user@host as well.

    Thanks for writing this up, was very useful.

    r.

  1. 1 Bash SSH known_hosts tab completion | Robin's home on the web


Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



Calendar

June 2011
M T W T F S S
« May   Jul »
 12345
6789101112
13141516171819
20212223242526
27282930  

Follow me

twitter flickr LinkedIn feed

Subscribe by email

Enter your email address:

Archives


Categories

Tag Cloud


Listening