Bash provides another environment variable called PROMPT_COMMAND. The contents of this variable are executed as a regular Bash command just before Bash displays a prompt.
[21:55:01][giles@nikola:~] PS1="[\u@\h:\w]\$ "
[giles@nikola:~] PROMPT_COMMAND="date +%H%M"
2155
[giles@nikola:~] d
bin mail
2156
[giles@nikola:~]
What happened above was that I changed PS1 to no longer include the
\t
escape sequence, so the time was no longer a part of the
prompt. Then I used date +%H%M
to display the time in a format
I like better. But it appears on a different line than the prompt.
Tidying this up using echo -n ...
as shown below works with
Bash 2.0+, but appears not to work with
Bash 1.14.7: apparently the prompt is drawn in a different way, and the
following method results in overlapping text.
2156
[giles@nikola:~] PROMPT_COMMAND="echo -n [$(date +%H%M)]"
[2156][giles@nikola:~]$
[2156][giles@nikola:~]$ d
bin mail
[2157][giles@nikola:~]$ unset PROMPT_COMMAND
[giles@nikola:~]
echo -n ...
controls the output of the date
command and
supresses the trailing newline, allowing the prompt to appear all on one
line. At the end, I used the unset
command to remove the
PROMPT_COMMAND environment variable.
You can use the output of regular Linux commands directly in the prompt as well. Obviously, you don't want to insert a lot of material, or it will create a large prompt. You also want to use a fast command, because it's going to be executed every time your prompt appears on the screen, and delays in the appearance of your prompt while you're working can be very annoying. (Unlike the previous example that this closely resembles, this does work with Bash 1.14.7.)
[21:58:33][giles@nikola:~]$ PS1="[\$(date +%H%M)][\u@\h:\w]\$ "
[2159][giles@nikola:~]$ ls
bin mail
[2200][giles@nikola:~]$
It's important to notice the backslash before the dollar sign of the command substitution. Without it, the external command is executed exactly once: when the PS1 string is read into the environment. For this prompt, that would mean that it would display the same time no matter how long the prompt was used. The backslash protects the contents of $() from immediate shell interpretation, so "date" is called every time a prompt is generated.
Linux comes with a lot of small utility programs like date, grep, or wc that allow you to manipulate data. If you find yourself trying to create complex combinations of these programs within a prompt, it may be easier to make an alias, function, or shell script of your own, and call it from the prompt. Escape sequences are often required in bash shell scripts to ensure that shell variables are expanded at the correct time (as seen above with the date command): this is raised to another level within the prompt PS1 line, and avoiding it by creating functions is a good idea.
An example of a small shell script used within a prompt is given below:
#!/bin/bash # lsbytesum - sum the number of bytes in a directory listing TotalBytes=0 for Bytes in $(ls -l | grep "^-" | cut -c30-41) do let TotalBytes=$TotalBytes+$Bytes done TotalMeg=$(echo -e "scale=3 \n$TotalBytes/1048576 \nquit" | bc) echo -n "$TotalMeg"
I have at times kept this both as a function, or as a shell script in my ~/bin directory, which is on my path. Used in a prompt:
[2158][giles@nikola:~]$ PS1="[\u@\h:\w (\$(lsbytesum) Mb)]\$ "
[giles@nikola:~ (0 Mb)]$ cd /bin
[giles@nikola:/bin (4.498 Mb)]$
You'll find I put username, machine name, time, and current directory name in most of my prompts. With the exception of the time, these are very standard items to find in a prompt, and time is probably the next most common addition. But what you include is entirely a matter of personal taste. Here are examples from people I know to help give you ideas.
Dan's prompt is minimal but very effective, particularly for the way he works.
[giles@nikola:~]$ cur_tty=$(tty | sed -e "s/.*tty\(.*\)/\1/")
[giles@nikola:~]$ echo $cur_tty
p4
[giles@nikola:~]$ PS1="\!,$cur_tty,\$?\$ "
1095,p4,0$
Dan doesn't like that having the current working directory can resize the prompt drastically as you move through the directory tree, so he keeps track of that in his head (or types "pwd"). He learned Unix with csh and tcsh, so he uses his command history extensively (something many of us weened on Bash do not do), so the first item in the prompt is the history number. The second item is the significant characters of the tty (the output of "tty" is cropped with sed), an item that can be useful to "screen" users. The third item is the exit value of the last command/pipeline (note that this is rendered useless by any command executed within the prompt - you could work around that by capturing it to a variable and playing it back, though). Finally, the "\$" is a dollar sign for a regular user, and switches to a hash mark ("#") if the user is root.
Torben Fjerdingstad (tfj@fjerdingstad.dk) wrote to tell me that he often suspends jobs and then forgets about them. He uses his prompt to remind himself of suspended jobs:
[giles@nikola:~]$ function jobcount {
> jobs|wc -l| awk '{print $1}'
> }
[giles@nikola:~]$ export PS1='\W[`jobcount`]# '
giles[0]# man ls &
[1] 4150
[1]+ Stopped (tty output) man ls
giles[1]#
Torben uses awk to trim the whitespace from the output of wc, while I would have used sed or tr - not because they're better, but because I'm more familiar with them. There are probably other ways as well. Torben also surrounds his PS1 string in single quotes, which prevent Bash from immediately interpreting the backquotes, so he doesn't have to escape them as I have mentioned.
NOTE: There is a known bug in Bash 2.02 that causes the jobs command (a shell builtin) to return nothing to a pipe. If you try the above under Bash 2.02, you will always get a "0" back regardless of how many jobs you have suspended. This problem is fixed in 2.03.