Useful bash scripts: Automatically changing terminal theme when using ssh on macOS
But we are hackers and hackers have black terminals with green font colors ~ John Nunemaker
This is the second in a series of posts on useful bash aliases and shell customisations that developers here at Talis use for their own personal productivity. In this post I describe how I configured my shell to automatically change my terminal theme when I connect to a remote machine in any of our AWS accounts.
Background
As I’ve mentioned previously, at Talis, we run most of our infrastructure on AWS. This is spread over multiple accounts, which exist to separate our production infrastructure from development/staging infrastructure. Consequently we can find ourselves needing to SSH onto boxes across these various accounts. For me it is not uncommon to be connected to multiple machines across these accounts, and what I found myself needing was a way to quickly tell which of these were production boxes and which were servers in our development account.
Solution
All of my development work is done on a Macbook Pro running macOS. Several years ago
I started using iTerm2 as my terminal emulator instead of the built in
terminal which has always felt particularly limited. Given these constraints
the solution I came up with was to implement a wrapper around an ssh
command that
would tell iTerm2 when to switch themes so that we can use different colors for
production environments vs development.
# Simple script that switches your iTerm2 Profile when using ssh.
# You'll need to export three environment variables:
# - SSH_DEFAULT_THEME
# - SSH_DANGER_THEME
# - SSH_WARNING_THEME
#
# The values for these must correspond exactly to the name of an iTerm Profile you have
# created. Within the Profile you can set the color/themes you wish to use.
#
# The script parses the hostname out of the ssh connection string (naive) and
# uses this in a case statement to set the theme. Add as many cases as you like.
#
# I've updated this recently to take advantage of iTerm's Badges feature to watermark
# my terminal session with the hostname. That way I can always tell what each session is
# connected to. For this to work I've inlined the iterm2_set_user_var function from
# the iterm shell integrations collection of tools.
#
# To use this either put this script or a symlink to it into /usr/local/bin
#
# author: ns@talis.com
# Github: kiyanwang
containsElement () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
iterm2_set_user_var () {
printf "\033]1337;SetUserVar=%s=%s\007" "$1" $(printf "%s" "$2" | base64 | tr -d '\n')
}
change_profile () {
NAME=$1; if [ -z "$NAME" ]; then NAME="$SSH_DEFAULT_THEME"; fi
#echo "Setting theme to $NAME"
echo -e "\033]50;SetProfile=$NAME\a"
iterm2_set_user_var current_ssh_host "$HOSTNAME"
}
on_exit () {
change_profile
iterm2_set_user_var current_ssh_host ""
}
trap on_exit EXIT
source ~/.aliases # awslookup is defined here
HOSTNAME=`echo $@ | sed s/.*@//`
PRODUCTION_EC2_HOSTS=(`awslookup production '*' text | awk -F'\t' '{ print $2"\n"$3 }'`)
STAGING_EC2_HOSTS=(`awslookup staging '*' text | awk -F'\t' '{ print $2 }'`)
if containsElement "$HOSTNAME" "${PRODUCTION_EC2_HOSTS[@]}"; then
change_profile "$SSH_DANGER_THEME"
elif containsElement "$HOSTNAME" "${STAGING_EC2_HOSTS[@]}"; then
change_profile "$SSH_WARNING_THEME"
else
case $HOSTNAME in # use this block to add any additional hosts
*.talisaspire.com) change_profile "$SSH_DANGER_THEME" ;;
*.talis.com) change_profile "$SSH_DANGER_THEME" ;;
esac
fi
echo "Connecting to $HOSTNAME"
/usr/bin/ssh "$@"
In order to work it requires you to create three profiles in iTerm2, and for the
purposes of this each of these profiles is essentially the theme you want to use.
When creating a profile you can customise colors, fonts etc. But crucially for
each of them you need to enter a value in the badge
field. This tells iTerm2 what
to set as the badge, which is displayed as a watermark on the terminal. In this case
I wanted to use the host of the machine that I’ve connected to which I specify as
current_user_host
in my script; therefore the value for the badge field needs
to be set to \(user.current_ssh_host)
.
When you’ve created the profiles you can add the following to your ~/.aliases
file to ensure that the ssh
wrapper script knows which profiles to use for the
three themes it requires.
export SSH_DEFAULT_THEME=Spacemacs
export SSH_DANGER_THEME=Production
export SSH_WARNING_THEME=Staging
Once this is done you can use the wrapper script. Do the following:
- copy the contents of the script to
/usr/local/bin/ssh
(or anywhere as long as it’s on yourPATH
) - now when you issue an
ssh
command in the terminal the script captures the hostname of the machine that you are trying to connect to - it then uses awslookup to check to see which AWS account that host resides in.
- in my case, if it’s in the production account it tells iTerm to switch to the
SSH_DANGER_THEME
, and if it’s in our development account it uses theSSH_WARNING_THEME
. - the terminal will then switch to the corresponding theme.
- when you exit your
ssh
session the wrapper resets the theme back to your default.
For example, when I ssh
to a production server, my terminal automatically
switches to this:
And when I connect to a development server, it automatically changes to this:
As soon as I exit the ssh
session the terminal is restored to my default theme.
Whilst this is a very specific solution for macOS you can achieve similar results on Linux. Enjoy!