## 4.6. Faster Prompt Setting with Built-ins

To set your prompt, you execute a command (on most shells, that command sets a shell variable). Before setting the prompt, you may run other commands to get information for it: the current directory name, for example. A shell can run two kinds of commands: built-in and external (Section 1.9). Built-in commands usually run faster than external commands. On a slow computer, the difference may be important -- waiting a few seconds for your prompt to reset can get irritating (though the computer would have to be quite slow nowadays for it to matter that much). Creative use of your shell's built-in commands might pay off there, and they are still quite useful for those trying to squeeze the most performance out of their system. Let's look at some examples:

- Section 4.3 has examples of some shells' special characters, such as `%D` to give the current date. Check your shell's manual page; if it has these features, using them won't slow you down the way an external command in backquotes (Section 28.14), like `'date'`, might.

- If you're putting your current directory in your prompt, you may only want the tail of the pathname (the name past the last slash). How can you edit a pathname? You might think of using basename (Section 36.13) or sed (Section 34.1) with the current directory from `$cwd` -- as in the first command in the following code listing, and probably in an alias like setprompt (Section 4.7) to make sure the prompt is updated whenever you change directories. The faster way is with the second command, using the C shell's built-in "tail" operator, `:t`:

  >     set prompt="`basename $cwd`% "

  `{}` Section 35.9

  >     set prompt="${cwd:t}% "

  If your current directory is /usr/users/hanna/projects, either of those prompts would look like "`projects%` " (with a space after the percent sign).

  The C shell has several of these built-in string operators (Section 28.5) like `:t`; the Korn Shell, zsh, and bash have more-powerful string operators (Section 28.5).

- If your prompt gets complex, you can use a shell function (Section 29.11) to access other built-in commands and edit the prompt. This can be faster than using an external shell or Perl script because functions run within the shell instead of in an external process. Here's an example from my .zshrc file:

  `${+}` Section 36.7, `$(...)` Section 28.14

  >     # Change "script" prompt automatically so I remember I'm in one.
  >     alias script='SCRIPT=yes /usr/bin/script'

  >     #
  >     # Functions:
  >     #
  >     setprompt( ) {
  >         case "${TTY##*/}" in
  >         tty[1-9]) xpi=':tty%l' ;;  # Virtual console
  >         *) xpi= ;;
  >         esac

  >         PS1="
  >     $USER@%m$xpi $(dirs)
  >     %* \$(folder -list)
  >     ${SCRIPT+SCRIPT-}%!%# "
  >     }

  Before the function, I set an alias that temporarily sets an environment variable named SCRIPT while the script (Section 37.7) program is running. The setprompt function, running in the child shell under script, sees that this environment variable has been set and adds the string `SCRIPT-` before the prompt. This reminds me that I'm logging to a script file. (If this is hard to visualize, Section 24.3 and Section 35.3 have some background.)

  The setprompt function itself has two parts. The first is a case statement (Section 35.11) that tests $TTY, the name of the tty (Section 2.7) I'm currently using. If the name ends in tty1, tty2, etc., it's one of my Linux virtual consoles (Section 23.12). In that case, I want to add the console name (tty1, etc.) to my prompt -- so I store the name in the xpi (extra prompt info) shell variable. This variable's value -- if it's been set -- is expanded when the prompt is printed. The second part sets the prompt variable PS1. The whole prompt looks like this:

  >     jpeek@kludge:tty1 ~/pt/art
  >     15:38:30 inbox pt
  >     501%

  The first line shows my username, hostname, the virtual console name (if any), and the current directory (in this example, there was nothing else on the directory stack (Section 31.7)). The second line has the time -- and my email folder stack, from the MH folder -list command, which is the only nonbuilt-in command used in the prompt. And here's a subtle point that's worth perusing. The whole prompt string is inside double quotes (Section 27.12) so that variable and command substitution will happen whenever setprompt is run. But, the way my prompt is set, the MH folder stack may change between the times that setprompt resets the prompt. So I escape the `$` in `\$(folder -list)`. This stores the command substitution without executing folder! So, when every prompt is about to be printed, the shell will evaulate the prompt string and expand the `$(...)` operators into the current folder stack. The third line sets the end of the prompt string: the zsh prompt substitution at `%m`, `%*`, `%!` and `%#`.

  On a slow machine, I'd try hard to find a way to eliminate the external folder -list command. But my Linux box is fast enough so that I don't notice any delay before a prompt. To make this work, I needed a good understanding of what's evaluated when. It's this sort of subtlety that makes prompt setting a challenge -- and a pleasure, too, when you get it working just right.

As another example, Section 4.14 shows more about using dirs in a shell prompt.

--JP and SJC

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