Thursday, March 13, 2008

[unix-geeky] : My UNIX shell prompt $;

It occurs to me that this blog could be, among other things, a place to share random unix tips and tricks that I've picked up over the years. Non-geek types are encouraged to go find a photography-related post or something. ;-) For the geek types still reading, here's a little something:

When choosing your prompt for any Bourne Shell derivative (sh, ksh, bash, zsh, and probably others I don't know about -- zsh is my current shell of choice, by the way), I find it handy to have my prompt follow this simple set of rules:
  1. the first character is a colon
  2. the second character is a space
  3. The last non-space character is a semicolon
  4. No other semicolons in the line (unless they're immediately followed by another colon and space)

Using the traditional "$" as a starting point, I might then put something like the following in my .profile (or .bash_profile or .zshrc or what have you -- .profile is the traditional Bourne shell location, so that's what I list):

PS1=": $; "

And thus a bit of my shell session might look like:

: $; 
: $;
: $; date
Thu Mar 13 19:01:28 PDT 2008
: $; pwd
: $; ls -CAF
: $; cat .profile
# this is a fake .profile file
# it would normally have a lot of content. For now, just this:
PS1=": $; "
export PS1 # possibly redundant
: $;
: $;
: $; : $; date
Thu Mar 13 19:04:13 PDT 2008
: $;

Now, why is this useful? And why does my prompt show up twice before that second call to the date command?

Well, as seasoned readers may already have guessed, there's some magic in this prompt. In Bourne-derived shells (as listed above), ':' is a shell-builtin command which does nothing more than have zero* as its exit status. Same deal as /bin/true, but in a single character command. And guess what? It takes arguments, and happily ignores them all. And as hopefully most of you (if you've bothered to read this far) already know, a semicolon separates commands. So that second time I ran date above, I did not get a double prompt from my shell, and I did not type my prompt in. No, instead what I did was a copy and pasted that command-line from earlier, by copying the whole line -- including the prompt. And it still worked, and didn't give me anything extraneous.

How cool is that?

Normally, I couldn't copy the whole line and still get desired behavior:
: $; PS1='$ '
$ date
Thu Mar 13 19:16:17 PDT 2008
$ $ date
zsh: command not found: $

(And it could be much worse than that, depending on what else is in your prompt.)

Starting with a colon and a space, and ending with a semicolon (and, for clarity only, another space), I can copy and paste the whole line.

My normal prompt is a lot longer than just : $; , and includes a bunch of different information that I find useful. Perhaps someday I'll write a blog entry about that. For now, just know that it always starts with a colon and a space, and ends with a semicolon (and a space). And whenever I write documentation which needs to include a shell prompt, the prompt shown will probably always be : $; (or maybe : #; , though that actually has problems of its own. Extra special feel-good (that's all you get) bonus points for those who can say why.)

Happy geeking!


*: Check it out:
: $; grep '\<0\>' /usr/include/sysexits.h
#define EX_OK 0 /* successful termination */

Thus, zero means successful exit.