## 4.16. Preprompt, Pre-execution, and Periodic Commands

bash, tcsh, and zsh can run a Unix command, or multiple commands, before printing each prompt. tcsh and zsh also can do something you specify before executing the command you've typed at a prompt. Finally, tcsh and zsh can do something periodically (every n seconds) before whatever prompt comes next. (Section 4.15 shows how to execute commands periodically in the original Bourne shell.) These commands don't have anything to do with setting the prompt itself, though they can. The command could do some system checking, reset shell variables, or almost anything that you could type at a shell prompt. If the commands run slowly, they'll delay whatever else you're doing, so keep that in mind.

Let's start with precmd, the tcsh alias that's run after your command line is read and before the command is executed. In zsh, the same thing is done by the shell function named preexec. Shell history is available, so you can use history substitution (Section 30.8) inside the alias or function. Here's a nice example adapted from the tcsh manual page: showing the command line you're running in your xterm window titlebar. It's ugly because it has ESC and CTRL-g characters embedded directly in the alias; I'd rather store the escape sequences in shell variables, as shown in the xterm titlebar article (Section 4.8). The if sets the alias only if you're using an xterm terminal:

>     # Show each command line in xterm title bar:
>     if ($TERM == xterm) alias postcmd 'echo -n "^[ ]2;\!#^G"'

Next, let's look at running a command periodically. You'd like to watch the load average by running uptime (Section 26.4) every minute, before a prompt. Here's how to do it in zsh: put code like this in your .zshrc file (Section 3.3) (or just type it at a prompt to try it). The PERIOD shell variable is the interval, in seconds, between runs of the periodic function as shown in the following code:

>     # Run "uptime" every 60 seconds; put blank line before:
>     periodic( ) {echo "\n==> $(uptime) <==";}
>     PERIOD=60

Here's how it looks:

>     jpeek@ruby$ pwd
>     /u/jpeek/pt

>     ==>   5:16pm  up  4:07,  6 users,  load average: 0.22, 0.15, 0.08 <==
>     jpeek@ruby$ lpr xrefs
>     jpeek@ruby$ mail -s "xrefs list" jan < xrefs

>     ==>   5:17pm  up  4:08,  7 users,  load average: 1.29, 0.55, 0.23 <==
>     jpeek@ruby$

Finally, here's how to set preprompt commands. These are run before each shell prompt is printed. In tcsh, define a precmd alias. In zsh, define a precmd function. In bash, store the command(s) in the PROMPT_COMMAND shell variable. Let's look at bash this time. Here's a silly example that I used to have in my bash setup file (Section 3.3):

`IFS` Section 36.23, `set` Section 35.25, `shift $#` Section 36.10

>     PROMPT_COMMAND='
>     # Save old $IFS; set IFS to tab:
>     OIFS="$IFS"; IFS="   "
>     # Put x in $1, face in $2, explanation[s] in $3[, $4, ...]:
>     set x `smiley`
>     # Put face into $face and explanation(s) into $explan:
>     face="$2"; shift 2; explan="$*"
>     # Restore shell environment:
>     shift $#; IFS="$OIFS"'

>     # Prompt I use (includes the latest $face):
>     PS1='\u@\h $face '

The first part is a series of shell commands that are stored in the PROMPT_COMMAND variable; they're surrounded by a pair of single quotes (`'' '`), one on the first line (after the `=`) and the other after `IFS` is reset. That series of commands is executed before every prompt. It sets two shell variables, `$face` and `$explan`, with new values before each prompt is set. The prompt is set on the last line; it includes the value of `$face`.

Here's what my screen looked like with this ridiculous setup. Notice that the prompt keeps changing as the PROMPT_COMMAND resets `$face` and `$explan`. If I wanted the explanation of a face I saw as I went along, I could type `echo <">$explan<">`:

>     jerry@ruby :-{) echo "$explan"
>     normal smiling face with a moustache
>     jerry@ruby +<||-) vi proj.cc
>        ...
>     jerry@ruby :-O echo "$explan"
>     Mr. Bill
>             Wow!
>             ohh, big mouth, Mick Jagger
>             uh oh
>     jerry@ruby :-)   < g++ -Wall proj.cc
>        ...

(It was even more useless than psychoanalyze-pinhead (Section 19.13), but it was fun while it lasted.) Seriously now, I'll say again: preprompt commands do not have to be used to set a prompt. You can use them to do anything. If the commands in PROMPT_COMMAND -- or any of the other functions or aliases we've covered -- write to standard output or standard error, you'll see that text on your screen, before or after the prompt, at the point where the commands are executed.

--JP and SJC

Copyright © 2003 O'Reilly & Associates. All rights reserved.