Friday, July 15, 2011

Using GNU Screen as your default shell

The title is a non sequitur, but we'll get to that later.

GNU Screen is an incredibly useful tool when you're working remotely over ssh on a linux server.  From their website:
Screen is a full-screen window manager that multiplexes a physical terminal between several processes, typically interactive shells.
Clear as mud?  Basically, it's a text-based window manager.  You can create multiple displays that you can switch between with a hotkey.  It also has clipboard functions and other features you're used to having in a graphical windowing interface, which are useful in a pure text environment, but for most people their graphical tools work just fine.  But Screen has another feature that makes it almost indispensable for working remotely.

What makes Screen especially useful is that you can "detach" from your display and "reattach" from somewhere else, and what's more, it automatically detaches if the connection drops and Screen keeps running.

Picture these two scenarios, which you have likely faced before:

1. You have some task that will take quite some time, like copying a terabyte of data to a backup drive on a remote server, but if you leave the connection idle the connection will drop.

2. You are working on a server on one machine, and need to move to another computer and pick up where you left off. For instance, working on the server console directly and then moving to your own computer, or vise-versa.

With Screen these scenarios are easy, if you just launch Screen first you can detach your session and pick it up from anywhere.


Quick and dirty Screen tutorial

Launch Screen:
$ screen

It creates a new shell for you, then use CTRL-A+D to detach.  That means press CTRL-A, then press D (not all three keys at once), all of the hotkeys to navigate through Screen use this format.  Screen exits, but your session is still running in the background.  Then launch Screen again with -r:
$ screen -r

-r means "reattach", it looks for a detached session and tries to reconnect to it.

If you have a long task to perform, or just have a flaky internet connection, you can launch Screen at the start to keep your session intact if your connection drops. If Screen loses you, it simply detaches so you can reattach it later.


But, there is one major problem:

The point where you realize you should use Screen is far too often the moment after you start a long task that can't be stopped. Screen is no help at all if you don't launch it, and if you only launch it when you know you'll need it, how are you supposed to remember to do it?

Wouldn't it be nice if Screen was your primary shell?  Then you'd never have to remember to launch it again.  We'll just change /etc/passwd to launch screen when we log in...  No!  Don't do that.  Because Screen is not a shell, it just launches your shell.  Screen will read your default shell from the passwd file and try to launch it, if your default shell is Screen... See the problem? I think it's smart enough to throw an error instead of launching itself again, but still, if you change your passwd file and log out you will not be able to log in again.

So instead, we can have it run automatically.

The easiest way to do that is to add it to our startup script.  But don't add it to the end of your .bashrc, that file is executed every time the shell launches, so again Screen will end up trying to launch itself in a loop when it tries to start bash.  Better would be .bash_profile, or .profile depending on your system.

There are some other things to consider.  Suppose, somehow, Screen can't launch?  It could happen, and then you'd be stuck.  Screen has many, many more options that what we've discussed, too, it's possible a configuration changes somewhere and the reattach command creates a new session for whatever reason.  To avoid any potential issues, I've created the following script to launch a primary Screen interface and always reconnect to the same one whenever I log in.

This is appended to the end of .bash_profile:
 ### Launch Screen automatically ###  
 echo   
 echo "Starting screen in 2 seconds, press Q to cancel.";  
 for i in 2 1 ; do   
   read -n 1 -t 1 -s a && break  
 done  
 set a = $a | tr '[A-Z]' '[a-z]'  
 if [ "$a" != "q" ]; then  
   screen -D -R main  
   logout  
   exit;  
 fi  
 echo "Canceled"  

When I login I get the message, "Starting screen in 2 seconds, press Q to cancel."  If I hit any key besides Q it skips to the end and launches screen, restoring my old session.  That way I don't have to wait two seconds every time I try to login.  If I hit Q it drops me to the regular bash prompt, my default shell, in case anything happens that would cause Screen to fail to launch.

To logout I don't type "exit" like I normally would, instead I hit CTRL-A+D to detach from Screen, which leaves my shell running for later, then continues the script above and logs out of my primary shell to close to connection.

The drawback is that I have to use Screen's history instead of my mouse-wheel to scrollback, which was hard to get used to at first and is still a little annoying, but I've gotten used to that.

This little script has saved my butt so many times, plus the convenience of returning to exactly where I left off is really helpful. Hopefully it can change your life like it did mine!

1 comment: