I've been using GNU screen for at least a couple years, but about 3 months ago, I changed my usage of it in a pretty major way, and I'd like to share my configs.
I'm not going to explain every feature in screen; others have already done it very well:
- http://www.debian-administration.org/articles/34
- http://jmcpherson.org/screen.html
- http://magazine.redhat.com/2007/09/27/a-guide-to-gnu-screen/
Making Screen Multi-Window Friendly
Like (I think) most people who use screen, I don't dig not being able to tell when I'm in a screen session. I also find it helpful to have named windows, and to have to names be easily accessible; the following line from my screenrc adds a helpful caption line:
caption always "%H %= %-w%L>%{= BW}%n*%t%{-}%52<%+w %L="
Which looks like so:

As you can see, this adds "tabs" at the bottom (although the screen term for them is 'windows'). Very soon after adding this, I had conventions for which windows were used for what - for a long time, I used 0 for htop, 1 for a general-purpose shell, 2 for music, 3 for emacs, and so on.
Why Use Multiple Sessions?
There are a couple problems with this. The first is that screen makes it pretty difficult to access windows above #9 - usually, you'd have to do prefix+space until you get to the window you want. Practically speaking, it meant that I hardly ever used more than 10 windows.
A second problem - exacerbated by the first - is that I would often have 5 or more windows dedicated to long-running processes - emacs, htop, an mpd client, a development server, and IRC. This left relatively little "working" room. I would run a build here, a download there, and all of a sudden I was out of available shells.
The next step I took was to separate the long-running process into a separate screen session. I would have one "processes" session and one "shells" session; screen -S allows you to name a session ("screen -S processes") which allows for easy reattaching ("screen -x processes") without having to deal with PIDs.
However, once I was running 2 screen sessions, why would I stop there? After all, if one of the processes somehow made screen crash (emacs would do this every so often), I'd lose the other processes too. And screen is extremely lightweight by modern standards; running 10 or 12 is not a problem for anything made in the last few years.
Using multiple sessions introduces a problem, however: how do you tell what session you're in?
Configs
Now, I want you to have fair warning: what I'm showing here is not
technically correct. I'm going to show you some stuff from my
.bashrc, which is not necessarily run when you start a screen
session. For example, screen -S emacs emacs -nw would only run emacs
inside of the screen session - so this wouldn't take effect.
Furthermore, every time you open a new shell, the bashrc will be run again. As long as you don't care about efficiency (and who does?) then that just means we have to be careful to make sure it's idempotent.
And a final warning: I really, really don't like writing shell. In fact, the reason it's taken me so long to write about this subject is that I'm more than a little embarrassed at the state of my bash knowledge. So all I can say about this code is that it "works for me" - not that it's correct, and certainly not that it's good.
So first, a utility function to determine if I'm in a screen session:
- _inside_screen()
- {
- # I hate bash.
- if [ "$STY" = "" ]; then
- echo "false"
- else
- echo "true"
- fi
- }
I can then modify the caption line that I posted above so that it uses
the $STY environment variable instead of the hostname.
- if [ `_inside_screen` = "true" ]; then
- # If I'm inside screen, set the caption to show STY so I know
- # which screen.
- screen -X caption always "$STY %= %-w%L>%{= BW}%n*%t%{-}%52<%+w %L="
- export HISTFILE=~/.bash_history.d/`_screen_name`.$WINDOW
- fi
Screen sets $STY to something like "12723.pts-3" by default; using
-S changes the last part of the value - for example, "12723.emacs".
Per-Window History
One thing that will start annoying you before long is the globally
shared history: next time you start up a project screen session, it's
history will be filled with commands from an arbitrary shell - the one
that exited last and wrote out ~/.bash_history last.
Instead, the above snippet sets HISTFILE to be inside a
~/.bash_history.d/ directory (which you'll need to create). Not
only does this keep your global history from getting jumbled up, but
it gives you very stateful shells - when you restart a named screen
session after a reboot, each window will have its pre-reboot history
available.
Using Sessions In A Workflow
After making the above changes, I started using one screen session per long running process, plus one session per project/context. for example, at home I have the following:

And that's about typical; I run about the same number of sessions at work, somewhat fewer on my laptop.
This makes my day-to-day workflow very nice - when starting work on a
project, I'll "screen -x
If you'd like to try it out, I'd recommend checking out my .screenrc, plus whatever other ones you can find to make your life easier. If there's one problem with screen, it's that the manpage is a rather intimidating wall of text - and it doesn't help that theres four different sets of commands to learn (the command-line options, the config file/command format, the default keybindings, and the caption/hardstatus format codes).
Comments
233 spam comments omitted.
I am no longer accepting new comments.
Andy MacKinlay
#24327, 2009-07-09T01:23:50Z
Hi,
nice tips, but I think you forgot to supply the _screen_name command...
Although I think ${STY#*.} does the right thing.
Rob
#24521, 2009-07-11T03:13:02Z
This is a really nice post. Like screen, this needs to be more popular. I think screen probably has the highest ratio of usefulness to adoption of any linux tool. I only just started using it and am finding it very labour-saving with only knowing the basics. This more advanced approach looks great, can't wait to try it. Keep up the good work
P.S. You should put a 'Digg this' button etc. on this blog, you'd reach a lot more people.
Tom ten Thij
#26206, 2009-08-05T07:15:42Z
I too was missing the _screen_name function. Here is one that seems to work for me:
_screen_name() { expr $STY : '..(.)' }
Morgan Goose
#26209, 2009-08-05T08:33:13Z
To better use screen for more than ten windows, use: \[HTML_REMOVED]-a \[HTML_REMOVED]-"
This will give you a list of screen that you can then use end, home, the number keys, and the arrow keys for.
The unique history is a good point, and its nice to know how to address it, but not having a complete history isn't a deal breaker for me.
(ED: fixed formatting)
Jacob
#26250, 2009-08-05T22:06:39Z
I disagree about screen making it hard to access beyond window #9.
1) Use ctrl+a ctrl+" or ctrl+a ctrl+'
2) Put this in your .screenrc:
#set bindings so we can easily access windows 10-18 bind ) select 10
bind ! select 11
bind @ select 12
bind # select 13
bind \$ select 14
bind % select 15
bind \^ select 16
bind & select 17
bind * select 18
bind ( select 19
(ED: fixed formatting.)
Adam Gomaa
#26376, 2009-08-06T16:50:42Z
Andy: It's not in the post, but it should be in the linked dotfiles.
cjh
#27072, 2009-08-14T22:42:50Z
unbelievable
Shane
#31735, 2010-01-08T13:17:54Z
Hi Andy - I didn't know about the $STY variable - makes it nice and easy to determine if we're in a screen session or now. Thanks!
I thought I'd offer a way to shorten and streamline your BASH syntax for easier use. Here we go:
ØL
#32431, 2010-01-21T11:55:22Z
I don't entirely agree with the difficulty of reacing screen windows above 9. I always use "(meta) #" to access a window. When the numbers run out (after 0) the next row is up q=11,w=12 etc. (I also mapped CAPSLOCK to meta, since capslock useless to me.)
Christopher Williams
#32446, 2010-01-21T21:17:06Z
Adam and Shane: here's an even shorter way to write the _in_screen function:
since a function returns the status of the last command that ran in it.
Also, bash (and probably every other Unix shell) is kind of strange with return statuses. It treats 0 as true and non-zero as false. This is because every well-behaving program in Un*x returns 0 for success and non-zero for error or failure. So in your (Shane) code, the logic in your _in_screen() function is actually backwards as a result of this peculiarity.
But other than that nit-pick, these are some pretty good tips for making screen even more awesome than it already is. :)