Dynamic Bash Prompts to Customize the Command Line

GNU/Linux users—and technically sophisticated users on other operating systems—spend a lot of time in the command-line interpreter. After each command, a little string called a prompt is displayed to signal that they can enter a new command. This prompt is, by default, a simple dollar sign or something else with minimal information. But the prompt is a very rich interface, particularly in the Bash shell provided as the default in GNU/Linux.

Not surprisingly, Bash was the first application mentioned by Linus Torvalds as running on his new kernel. Because Bash, like most elements of the operating system Torvalds built on Linux, is a product of the GNU project, I consider it correct to refer to the operating system as GNU/Linux.

You can set a prompt that reminds you what environment you're working with—the date and time, for instance, or your current directory. I find myself regularly checking the current date, so I've set my prompt to display it. With Bash, you can go even further, displaying a wealth of information taken from the environment. You can execute a whole pipeline of commands in your prompt. I'll show you how to do that in this article.

Some Simple Customizations

Before we sink our teeth into Bash's most dazzling options for prompts, let's look at some basics. These work in most shells, not just Bash.

Many users issue the same command repeatedly. To make this easier, shells keep a command history, assigning a number to each command as you enter it. If you happen to need to reissue your 41st command, you can type:

!41

All very well, but how would you know that this much beloved command is the 41st? You can display the command number in the prompt like this:

PS1="\! "

I hope this terse nexus of odd characters hasn't scared you. It's not so awful—let's take it apart.

First, what is PS1? It stands for "Prompt string 1." Every time a command finishes, Bash displays the string you specify here.

As the number 1 hints, there are other prompts in Bash—four in all. In addition to PS1, I sometimes find PS2 useful. Whereas PS1 appears every time a command appears, PS2 appears when you enter a partial command. I'll explain that later.

You also need to understand that your Bash environment contains environment variables, also known as parameters. I'll show you soon how to view those parameters. You can set parameters by specifying them followed by an equal sign. So when you type the following  into the shell:

PS1="Hi! Tell me what to do: "

you have just set the PS1 parameter. Note that I've put a blank space before the final quote. That gives some pleasant room after each prompt.

But the \! string is really mysterious. The key to understanding it is to focus on the initial backslash, which has powerful properties in many commands. Check the Bash documentation (which you can view through the man bash command) to find out that \! plugs in the current command number. A section of the documentation named PROMPTING lists all the magic things enabled by backslashes.

Now for my quest to display date and time in my prompt. The PROMPTING section lists lots of options. For instance, if you are a nervous Type A person concerned with the inexorable passage of time, you can set a prompt like this:

PS1="Stay alert: Time is \T "

The magic \T plugs in the current time right down to the second, so that a sample prompt looks like:

Stay alert: Time is 07:16:53

I want more fine-grained control over my date and time display, so I take the next step and use the flexible \D format (where D stands for date). The advantage of this format is that you can create the precise string you want. But that adds some complexity.

The Bash documentation circuitously tells you that the \D format follows the rules of the standard strftime library function. This might scare off non-programmers, but don't despair. Go ahead and enter man strftime. You will find there that you can specify elements of date and time just as with Bash. But you use a percent sign (%) instead of a backslash. For instance, to display an abbreviation of the current day, use %a. Following the man page, I created the following cryptic string:

PS1='\D{%a, %b %e %l:%M }'

I've simply consulted the strftime man page to get a bunch of strings that cull the date and time from the environment. The command is sensitive to your environment, reflecting your country, language, and time zone. A typical prompt created by this setting on my U.S. English system looks like:

Tue, Sep 22 11:14

I hope this has piqued your interest in creating prompts that reflect your needs. Let's plunge in further!

Dynamic Command Execution and Other Advanced Features

Bash goes far beyond other shells to let you perform a range of calculations and commands in your prompt. I'm going to build up a PSS2 prompt you may find useful in this section, along with some other advanced prompts.

PS2 appears when you enter a command that  has multiple lines, or a line that ends with a quote, backslash, or other shell character that requires more input. Try, for instance, the following sequence at the command prompt:

echo "
I'm leaving space around this text
"

Because the first quotation mark indicates that you're starting a quote, Bash waits for you to enter more text, completing and executing the command only when you terminate the quote with a second quotation mark. The result of this echo command is a string that begins and ends with blank lines.

Another type of command that causes PS2 to appear is a compound command, which allows programming in the shell. I won’t explain compound commands in this article, but here is an example to show how they can use multiple lines:

if
  test -f datafile.csv
then
  cp datafile.csv ~/tmp
else
  echo "datafile.csv not found"
fi

To let you know when you're inside a command, Bash displays PS2. I'm going to change this prompt to show you the string that began the sequence. This will take some dextrous combinations of commands.

How do I show the command that I'm in right now? I can use Bash's command history. We saw that in the previous section. You can see all the commands saved in the history through this command:

history

But you can also limit the display to the most recent commands. I can reduce the output to the current command with:

history 1

This displays the number of the command, which I used in the previous section, as well as the command. I want to strip out the distracting number, which occupies the first seven characters of the line. So I'll pipe the history output to the cut command as follows. It cuts the first seven characters and displays everything starting from the eighth character:

history 1 | cut -c8-

Now we have to understand how to embed commands. Shells use backticks (the obscure and rarely used ` character) to embed one command inside another. So you can embed the previous pipeline with:

`history 1 | cut -c8-`

Bash also allows the following format, which is more verbose but may be easier to read:

$(history 1 | cut -c8-)

Using the backtick format, I embed the command as follows:

PS2='`history 1 | cut -c8-` '

Can you unpack that command? It starts with PS2= to set the prompt. The following string is enclosed in apostrophes (which work better here than quotation marks). Within the apostrophes, I use backticks to embed the command pipeline.

My solution works pretty well in some situations, and less well in others—you may want to play with it to see whether you can make it meet your needs. Here’s what terms up on my terminal when I enter the compound command I showed earlier. My input is bold, while the prompt is plain computer font:

if
if test -f datafile.csv
if test -f datafile.csv then
if test -f datafile.csv; then cp datafile.csv ~/tmp
if test -f datafile.csv; then cp datafile.csv ~/tmp else
if test -f datafile.csv; then cp datafile.csv ~/tmp; else echo "datafile.csv not found"
if test -f datafile.csv; then cp datafile.csv ~/tmp; else echo "datafile.csv not found" fi

More Substitutions

We just saw command substitution in a prompt. Bash allows several other types of substitution in both commands and prompts. We'll look at a couple here to wrap up this article.

Try entering env in your shell. You'll get a listing of all the parameters, many of which will be mysterious. You'll recognize HOME, which is your home directory. USER is your user name. SHELL should be a path to "bash"—if it's something different, you're not running Bash and many of the advanced techniques in this article won't work!

To illustrate more fancy prompt settings, I'll focus on the shell level. What if you enter the bash command at the shell? You run one shell inside another. You are moving from shell level 1 to shell level 2. And if you type bash again, you'll be at shell level 3. (Return to a lower level by typing exit.)

Most users don't need to run embedded shells. If you want to temporarily log in as root to do some administrative task, an embedded shell is useful. I ran embedded shells a lot while trying out features for this article. So let's suppose that you use embedded shells and want to be reminded what level you're in.

The $SHLVL parameter indicates the shell level. If you enter:

echo $SHLVL

you'll normally see 1, the most basic level.

I'm going to embed the shell level into my prompt through:

PS1='Working directory \w in shell $SHLVL: '

The \w is a simple prompt variable that plugs in the directory I'm in. The $SHLVL parameter is also interpreted, leading to a prompt such as:

Working directory ~/bin in shell 2:

To end this discussion of prompt strings, I'm going to imagine a rather bizarre need. For most people, it makes sense to use 1 for the most basic shell level. But let's suppose you like to use 0 as the most basic level. (Many programming languages count from zero, and it has become a kind of quirk among programmers.) You could do some arithmetic substitution in the prompt, using the Bash syntax of a dollar sign followed by doubled parentheses:

PS1='Working directory \w in shell $(($SHLVL-1)): '

Within those doubled parentheses, I've subtracted 1 from the shell level.

Conclusion

I hope the examples in this article have alerted you to command-line conveniences you haven't thought of before. Check some man pages, think about the information you would like at your fingertips as you run commands, and go ahead—empower your prompt.

Comments