Distinguish local and remote terminals
- Last updated
If you're a heavy user of the command-line, you may have run the wrong command in the wrong terminal window once or twice by accident. This can, of course, have unexpected consequences especially when connected to a production server.
The common utilities like terminal emulators, shells and SSH clients don't really provide anything to prevent these kinds of mistakes. At best, you get a prompt with different username and hostname but telling this apart from your local prompt can be difficult at a glance.
Is there a better way to quickly identify the correct window? In this article we're going to use colors indicate the remote terminal session:
The only terminal emulator I'm aware of that does something similar out of the box is King's Cross. It changes the title bar background color when using SSH or the root user. This article's approach is more configurable and will work with a number of different terminal emulators.
Changing background color
You can usually configure terminal emulator's color scheme to your liking by using a GUI or modifying some configuration file. In addition, many terminal emulators support escape sequences to change colors on the fly. For example, running the following command should change your terminal's background color to a delightful red:
$ printf "\033]11;rgb:ff/00/00\033\\"
If the new color hurts your eyes, reset the background to its original color using:
$ printf "\033]111\033\\"
It's possible that these commands did not work for you. If this is the case, you should:
- Request this feature from the authors of the terminal emulator or, if possible, contribute the support yourself
- Investigate if there are other ways of programmatically changing the color scheme in the terminal emulator of your choice
- Switch to a terminal emulator that supports these escape sequences, such as Alacritty or any VTE-based terminal emulator
Now that we hopefully have the commands to change the background color, we just need to find a way to run them automatically when using a command like
There are a couple of ways to accomplish this:
- SSH has an option called
LocalCommandwhich can be used to change the background color after connecting to a server. Unfortunately, there isn't the opposite option that could be used to reset the background color after disconnecting.
- The background change command can be put in
.bashrcfile on the server. Similarly, the background reset can be done in
.bash_logout. The downside of this approach is that we need to do it for each server.
The best approach, in my opinion, is to create a wrapper for
This is a flexible solution that should work in any situation.
Here's the first attempt:
#!/bin/sh printf "\033]11;rgb:55/11/11\033\\" ssh "$@" # Pass arguments to the real SSH executable printf "\033]111\033\\"
This script works in normal circumstances. However, if the script is interrupted (e.g. with Ctrl+C), the background color is not reset. To fix this, let's add a trap to always reset the background color after exit:
#!/bin/sh trap 'printf "\033]111\033\\"' EXIT INT printf "\033]11;rgb:55/11/11\033\\" ssh "$@"
There's still one corner case we need to handle: using
ssh with pipelines and redirects.
This can be a convenient way of transferring data:
$ ssh server 'mysqldump db_name' > backup-file.sql
In this case, we do not want any escape sequences to mess up our backups or other data.
[ -t 1 ] we can detect whether file descriptor 1, i.e. the standard output, is attached to a terminal.
This gives us the final script:
#!/bin/sh if [ -t 1 ]; then trap 'printf "\033]111\033\\"' EXIT INT printf "\033]11;rgb:55/11/11\033\\" fi ssh "$@"
To use the wrapper, store the script in a file like
~/bin/myssh and run
chmod +x ~/bin/myssh to make it executable.
alias ssh=~/bin/myssh to replace
ssh command with the wrapper.
Now try connecting to a server using the regular
ssh server and you should see the background change.
Finally, add the alias to your
.bashrc (or equivalent file for your shell) to enable the wrapper permanently.
Although we have a working solution, there are still many things we can tweak. First and foremost, you should find a background color that works well with your current color scheme.
Secondly, the same background color is used for all sessions. It may be helpful, for instance, to differentiate development, staging and production servers from each other. We can change the background color based on the name:
# Match substring in arguments like "-p 1234 server1-prod uname -a" case "$@" in *-prod* | *myserver.com* ) printf "\033]11;rgb:55/11/11\033\\" ;; *-stg* | *staging.myserver.com* ) printf "\033]11;rgb:11/55/11\033\\" ;; *-dev* | *localhost* ) printf "\033]11;rgb:11/11/55\033\\" ;; * ) printf "\033]11;rgb:55/11/55\033\\" ;; esac
The text may be hard to read if only the background color is changed. Conveniently, there are additional escape sequences that can be used to change the whole color scheme on the fly. For example, to use the excellent Solarized Light color scheme, run the following commands:
printf "\033]10;rgb:65/7b/83\033\\" # set foreground color printf "\033]11;rgb:fd/f6/e3\033\\" # set background color printf "\033]12;rgb:58/6e/75\033\\" # set text cursor color printf "\033]13;rgb:58/6e/75\033\\" # set mouse foreground color printf "\033]14;rgb:93/a1/a1\033\\" # set mouse background color printf "\033]4;0;rgb:ee/e8/d5\033\\" # set black printf "\033]4;1;rgb:dc/32/2f\033\\" # set red printf "\033]4;2;rgb:85/99/00\033\\" # set green printf "\033]4;3;rgb:b5/89/00\033\\" # set yellow printf "\033]4;4;rgb:26/8b/d2\033\\" # set blue printf "\033]4;5;rgb:d3/36/82\033\\" # set magenta printf "\033]4;6;rgb:2a/a1/98\033\\" # set cyan printf "\033]4;7;rgb:07/36/42\033\\" # set white printf "\033]4;9;rgb:cb/4b/16\033\\" # set bright black printf "\033]4;8;rgb:fd/f6/e3\033\\" # set bright red printf "\033]4;10;rgb:93/a1/a1\033\\" # set bright green printf "\033]4;11;rgb:83/94/96\033\\" # set bright yellow printf "\033]4;12;rgb:65/7b/83\033\\" # set bright blue printf "\033]4;13;rgb:6c/71/c4\033\\" # set bright magenta printf "\033]4;14;rgb:58/6e/75\033\\" # set bright cyan printf "\033]4;15;rgb:00/2b/36\033\\" # set bright white
Run the following to restore the original color scheme:
printf "\033]110\033\\" # reset foreground color printf "\033]111\033\\" # reset background color printf "\033]112\033\\" # reset text cursor color printf "\033]113\033\\" # reset mouse foreground color printf "\033]114\033\\" # reset mouse background color printf "\033]104\033\\" # reset other colors
Changing and managing color schemes using raw escape sequences may become unwieldy. Instead, you can use utilities like theme.sh, pywal and xtermcontrol to make things easier.
Understanding escape sequences
How do these escape sequences work?
Terminals basically display text but with special sequences we can send commands to do more interesting things, such as controlling output formatting or building complete TUIs.
Let's break down the familiar escape sequence
\033]are two characters that mark the start of Operating System Command (OSC). Specifically,
\033is the number 27 in octal, i.e.
ESCor escape character in ASCII, and
]is just the right square bracket it looks like.
\033is a special syntax supported by
11is the numeric identifier for command that changes the background color of the terminal.
rgb:ff/00/00is the first and only argument. The format is based on
XParseColorfunction from XLib. More familiar
#ff0000syntax is also supported but its use is not encouraged.
ESCfollowed by the backslash
\. This two character sequence is known as the string terminator (ST) and here it marks the end of OSC.
\\is the way of escaping the backslash character in a double-quoted string.
With this in mind, the other escape sequences presented in this article should not look too cryptic anymore. These escape sequences among others are documented thoroughly in XTerm Control Sequences.