Jun 2007

A Sample Function inline with Bashprompt

In a recent text configuring colors for the Bash prompt was discussed. In this text a method of messing with the prompt is expanded upon by actually inserting a shell code function inline with the bash prompt.

A Basic Bash Prompt

The bash prompt has built in commands that use escape character syntax. By default a lot of packages ship bash with a default that shows the hostname and relative path. The escape sequences for all users is usually kept in /etc/bashrc or a similar system location. The sequences themselves might look something like this:

PS1="[\u@\h:\w]\$ 

The above settings would look like:

[username@hostname:current_working_directory]$

Or a real example:

[mui@vela:~/www/systhread.net/texts]$

The Bash Prompt HOWTO is a great resource for looking at different things that can be done with a prompt. Here are the escape sequences per the Bash man page that can be used:

  When  executing  interactively,  bash displays the primary
       prompt PS1 when it is ready to read  a  command,  and  the
       secondary  prompt PS2 when it needs more input to complete
       a command.  Bash allows these prompt strings  to  be  cus
       tomized by inserting a number of backslash-escaped special
       characters that are decoded as follows:
              \a     an ASCII bell character (07)
              \d     the date  in  "Weekday  Month  Date"  format
                     (e.g., "Tue May 26")
              \e     an ASCII escape character (033)
              \h     the hostname up to the first `.'
              \H     the hostname
              \j     the  number of jobs currently managed by the
                     shell
              \l     the basename of the shell's terminal  device
                     name
              \n     newline
              \r     carriage return
              \s     the  name  of  the shell, the basename of $0
                     (the portion following the final slash)
              \t     the current time in 24-hour HH:MM:SS format
              \T     the current time in 12-hour HH:MM:SS format
              \@     the current time in 12-hour am/pm format
              \u     the username of the current user
              \v     the version of bash (e.g., 2.00)
              \V     the release of bash,  version  +  patchlevel
                     (e.g., 2.00.0)
              \w     the current working directory
              \W     the  basename  of the current working direc­
                     tory
              \!     the history number of this command
              \#     the command number of this command
              \$     if the effective UID is 0, a #, otherwise  a
                     $
              \nnn   the  character  corresponding  to  the octal
                     number nnn
              \\     a backslash
              \[     begin a sequence of non-printing characters,
                     which could be used to embed a terminal con
                     trol sequence into the prompt
              \]     end a sequence of non-printing characters

Basic escape sequences are pretty nice and Bash is feature rich with them. For example look at the three different time formats:

 \t     the current time in 24-hour HH:MM:SS format
 \T     the current time in 12-hour HH:MM:SS format
 \@     the current time in 12-hour am/pm format

Just by default the bash prompt can be very informative but what if something a little bit more is needed?

External Commands

As noted in the Bash Prompt HOWTO commands can be executed every time the RETURN sequence is sent to the shell interpreter. For this example the Bash Prompt HOWTO provides ample demonstration of some commands:

#!/bin/bash
#     lsbytesum - sum the number of bytes in a directory listing
TotalBytes=0
for Bytes in $(ls -l | grep "^-" | awk '{ print $5 }')
do
    let TotalBytes=$TotalBytes+$Bytes
done
TotalMeg=$(echo -e "scale=3 \n$TotalBytes/1048576 \nquit" | bc)
echo -n "$TotalMeg"

will yield the results in the author's directory of:

[2158][giles@nikola:~]$ PS1="[\u@\h:\w (\$(lsbytesum) Mb)]\$ "
[giles@nikola:~ (0 Mb)]$ cd /bin
[giles@nikola:/bin (4.498 Mb)]$

Pretty neat stuff. The bashrc can also contain functions that are used in the prompt itself.

Fun with XTerms
Using Complete Functions Within bashrc

Entire functions can be put right into the .bashrc file for similar command processing. Following is an example function:

rand_titles ()
{
    if [ $TERM = xterm ]; then
        TLINES=`wc -l $HOME/.xterm_titles | awk '{print $1;}'`
        TLINE=$[ ${RANDOM} % $TLINES ];
        TLINE=$[$TLINE + 1];
        TITLE=`head -$TLINE < $HOME/.xterm_titles | tail -1`
        echo -ne "\033]0;${TITLE}\007"
    fi
}

Line by Line

  1. function declaration followed by the opening brace.
  2. If the terminal matches xterm [ 1 ] then keep going.
  3. Count the number of lines in the .xterm_titles file.
  4. Use the RANDOM environment variable to calculate a random number within the TLINES range. Assign said number directly to TLINE
  5. Add one to TLINE.
  6. Using the new TLINE extract a line from .xterm_titles and assign it to TITLE.
  7. Echo the resulting line into the prompt (the TITLE variable).

Essentially rand_titles just picks a random line out of a file and feeds to the current xterm program as a new title.

Inserting The Function

Not as difficult as it would seem; the call is just slid right into the prompt command itself:

PROMPT_COMMAND='rand_titles'

Essentially - any Unix software that can receive data from a pipe can be affected by bash prompt functions.

Credits

The text would be remiss no credit was given where due. I originally figured all of this out by downloading Carsten Haitzler's (aka Rasterman) files section [2]. Ironically the example used was for the Z shell zsh [ 3 ] so there was a little translating going on.

.

Cautionary Statement

Be Warned! messing with the prompt, especially if you are inserting entire functions into it can be dangerous. If you do not have administrator access (as root or some delegated role) to the system then there are a few things to consider.

If a test Account can be wrangled - then do so

Often, power users are allowed to request test accounts. If playing with functions built into prompts is on the agenda then do so. Get a test account that can be switch-user'd to for testing.

Backup the bashrc

One of the nice things about bash is that the bashrc can be resourced on the fly for testing. Basically take the following steps to make sure that the original bashrc can be restored in case of a mistake:

  1. cd $HOME
  2. cp .bashrc .bashrc.ORIG
  3. Make sure there is another terminal open with the same account.

Changes can be experimented with by simply sourcing the changed bashrc:

source .bashrc

Note that a current bashrc can also just be copied to a new one, then source the test bashrc to see if there are any problems:

cp .bashrc bashtest
(makemany changes...)
source bashtest...
... pray2 $DEITY

Be Smart About Functions

Test functions before inclusion in the prompt. If a system has a lot of NFS mounts then do not include a global df command in the prompt [5]. Keep things like how much will this cost and can it hang? in mind when dealing with prompt commands.

Summary

Customizing and mastering the Unix shell environment can make for not just a pleasant experience as a user but a practical experience for users.

Resources
Footnotes
  1. Note that many terminal programs qualify as xterminals such as putty.
  2. There are a lot of goodies that can be found over at Rasterman's files section for the very patient.
  3. The Z shell zsh has given me serious reason to think about trading off shells for awhile even just for fun.
  4. Believe it or not I worked at a company that had df output in every user's login files.