Bash SSH Happiness

So Dennis? (whose name doesn’t appear anywhere on his blog except for in the URL) wrote a cool bash script to create aliases for every host in your ~/.ssh/known_hosts file. I found some bugs/problems so I spiffed it up a bit. As the comments say, you should pipe the output to sort(1). I’ve tried to make the text as small as possible, but it won’t all display. I tested selecting it and even the parts that don’t display end up in my clipboard, so there ya go.

#!/bin/bash
shopt -s extglob
isint(){
    case $1 in
        ?([-+])+([0-9]) )
            return 0;;
        *) return 1;;
            esac
}

if [[ -d ~/.ssh ]]; then

    # Touch files we expect to exist
    if [[ ! -e ~/.ssh/config ]]; then touch ~/.ssh/config; fi
    if [[ ! -e ~/.ssh/known_hosts ]]; then touch ~/.ssh/known_hosts; fi

    # Parse ~/.ssh/known_hosts and ~/.ssh/config to find hosts
    for x in `sed -e 's/[, ].*//' ~/.ssh/known_hosts; awk '/^Host [^*?]+$/{print $2}' ~/.ssh/config`; do

        # Don’t override commands
        type "${x}" > /dev/null 2>&1 && continue

        # Remove the domainname
        y=${x%%.*}
        # you don't want IP addresses for aliases, trust me.
        isint $y && continue

        # If it's a short-name, move on
        #z=${x##*.}
        #[[  "${z}" == 'edu' ||  "${z}" == 'com' || "${z}" == 'net' ]] || continue
        # So the above is commented out because you'd be surprised at how much 
        # you rely on your search path. You should pipe the output of this script to
        # sort and your fqdn's will override your shorts.
        echo alias "${x}"=”ssh $x”

        if [[ "$y" != "$x" ]]; then
            if ! type $y > /dev/null 2>&1; then
            echo     alias $y=”ssh $x”
            fi
        fi
    done
fi

Dennis also mentions that you might want to add HashKnownHosts no to your ~/.ssh/config file. If you have some hashed keys in your file, you should remove them before running this script. Sadly I couldn’t find any way to programmatically convert a hashed file into a non-hashed file. But to make up for it, here’s a bonus alias!

# removes _exactly what you type_ from ~/.ssh/known_hosts
# meaning 'grapes' gets you the key for 'grapes' vs 'grapes.wrath.com'
alias forget="ssh-keygen -R"

Bash & Screen

You may be familiar with my tutorial on getting your ssh-agent to work inside screen. If not, have a look.

There’s always room for improvements! Here’s an excerpt from my current .bash_profile:

function Attach(){
    grabssh
    if [[ -z "${1}" ]] ; then
        local n=`screen -wipe | egrep -i 'attached|detached' | wc -l`
        if [[ "${n}" -gt 1 ]]; then
            check_screen
            return
        fi
    fi
    echo screen -d -R ${*}
    screen -d -R ${*}
}

check_screen () {
  # Look in the path?
    type screen > /dev/null 2>&1
    if [[ ${?} = 0 ]]; then
        tmp=0
        echo
        for scr in `screen -wipe | egrep -i 'attached|detached' | awk '{print $1"_"$2}'`
        do
            echo "Screen available: ${scr}"
            if [[ ${tmp} -eq 0 ]] ; then
                myscreen=${scr%_*}
            fi
            tmp=$(($tmp+1))
        done
        if [[ -n "${myscreen}" ]] ; then
            echo
            echo "Enter to attach to ${myscreen},"
            echo "'n' to move on,"
            echo "unique bits to attach elsewhere"
            read eon
            if [[ -z "${eon}" ]] ; then
                [[ -z "${myscreen}" ]] && return
                eon=${myscreen}
            fi
            if [[ "${eon}" != 'n' ]] ; then
                Attach ${eon}
            fi
        fi
    fi
}

[[ -n "$PS1" ]] && check_screen

If screen(s) exist, I’m presented with a list of them on login. I’m then able to hit return for a default, or type in which of them I wish to resume.

If I don’t want to resume any screen I can simply hit ‘n’ and go on about my day.

The Attach and check_screen functions are actually in a different file that is sourced by .bashrc and conditionally by .bash_login. This allows the functions to be used in non-login shells (in local terminal windows for example).