# This function performs host completion based on ssh's known_hosts files, # defaulting to standard host completion if they don't exist. # _known_hosts() { local cur ocur user suffix aliases global_kh user_kh hosts local -a kh config COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} ocur=$cur [ "$1" = -a ] || [ "$2" = -a ] && aliases='yes' [ "$1" = -c ] || [ "$2" = -c ] && suffix=':' [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@} kh=() # ssh config files [ -r /etc/ssh/ssh_config ] && config[0]=/etc/ssh/ssh_config [ -r ~/.ssh/config ] && config[1]=~/.ssh/config if [ ${#config[@]} -gt 0 ]; then # expand path (if present) to global known hosts file global_kh=$( eval echo $( sed -ne 's/^[Gg][Ll][Oo][Bb][Aa][Ll][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['$'\t '']*\(.*\)$/\1/p' ${config[@]} ) ) # expand path (if present) to user known hosts file user_kh=$( eval echo $( sed -ne 's/^[Uu][Ss][Ee][Rr][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['$'\t '']*\(.*\)$/\1/p' ${config[@]} ) ) fi # choose which global known hosts file to use if [ -r "$global_kh" ]; then kh=( "$global_kh" ) else [ -r /etc/known_hosts ] && kh[0]=/etc/known_hosts [ -r /etc/known_hosts2 ] && kh[1]=/etc/known_hosts2 fi # choose which user known hosts file to use if [ -r "$user_kh" ]; then kh=( ${kh[@]} "$user_kh" ) else [ -r ~/.ssh/known_hosts ] && kh=( ${kh[@]} ~/.ssh/known_hosts ) [ -r ~/.ssh/known_hosts2 ] && kh=( ${kh[@]} ~/.ssh/known_hosts2 ) fi # If we have known_hosts files to use if [ ${#kh[@]} -gt 0 ]; then # Escape slashes and dots in paths for awk cur=${cur//\//\\\/} cur=${cur//\./\\\.} if [[ "$cur" == [0-9]*.* ]]; then # Digits followed by a dot - just search for that cur="^$cur.*" elif [[ "$cur" == [0-9]* ]]; then # Digits followed by no dot - search for digits followed # by a dot cur="^$cur.*\." elif [ -z "$cur" ]; then # A blank - search for a dot or an alpha character cur="[a-z.]" else cur="^$cur" fi # FS needs to look for a comma separated list COMPREPLY=( $( awk 'BEGIN {FS=","} {for (i=1; i<=2; ++i) { \ gsub(" .*$", "", $i); \ if ($i ~ /'$cur'/) {print $i} \ }}' ${kh[@]} ) ) # append any available aliases from config files if [ ${#config[@]} -gt 0 ] && [ -n "$aliases" ]; then hosts=$( compgen -W "$( echo $( sed -ne "s/^[Hh][Oo][Ss][Tt]["$'\t '"]*\([^*?]*\)$/\1/p" ${config[@]} ) )" -- $ocur ) COMPREPLY=( ${COMPREPLY[@]} $hosts ) fi # apply suffix for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do COMPREPLY[i]=$user${COMPREPLY[i]}$suffix done else # Just do normal hostname completion COMPREPLY=( $( compgen -A hostname -S "$suffix" -- $cur ) ) fi return 0 } complete -F _known_hosts traceroute ping fping telnet host nslookup rsh \ rlogin ftp # ssh(1) completion # _ssh() { local cur prev local -a config COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} case "$prev" in -*c) COMPREPLY=( $( compgen -W 'blowfish 3des 3des-cbc blowfish-cbc \ arcfour cast128-cbc' -- $cur ) ) ;; -*l) COMPREPLY=( $( compgen -u -- $cur ) ) ;; *) _known_hosts -a [ $COMP_CWORD -eq 1 ] || \ COMPREPLY=( ${COMPREPLY[@]} $( compgen -c -- $cur ) ) esac return 0 } shopt -u hostcomplete && complete -F _ssh ssh slogin sftp