33.5. "Colorizing" Scripts

The ANSI [1] escape sequences set screen attributes, such as bold text, and color of foreground and background. DOS batch files commonly used ANSI escape codes for color output, and so can Bash scripts.


Example 33-11. A "colorized" address database

   1 #!/bin/bash
   2 # ex30a.sh: "Colorized" version of ex30.sh.
   3 #            Crude address database
   4 
   5 
   6 clear                                   # Clear the screen.
   7 
   8 echo -n "          "
   9 echo -e '\E[37;44m'"\033[1mContact List\033[0m"
  10                                         # White on blue background
  11 echo; echo
  12 echo -e "\033[1mChoose one of the following persons:\033[0m"
  13                                         # Bold
  14 tput sgr0
  15 echo "(Enter only the first letter of name.)"
  16 echo
  17 echo -en '\E[47;34m'"\033[1mE\033[0m"   # Blue
  18 tput sgr0                               # Reset colors to "normal."
  19 echo "vans, Roland"                     # "[E]vans, Roland"
  20 echo -en '\E[47;35m'"\033[1mJ\033[0m"   # Magenta
  21 tput sgr0
  22 echo "ones, Mildred"
  23 echo -en '\E[47;32m'"\033[1mS\033[0m"   # Green
  24 tput sgr0
  25 echo "mith, Julie"
  26 echo -en '\E[47;31m'"\033[1mZ\033[0m"   # Red
  27 tput sgr0
  28 echo "ane, Morris"
  29 echo
  30 
  31 read person
  32 
  33 case "$person" in
  34 # Note variable is quoted.
  35 
  36   "E" | "e" )
  37   # Accept upper or lowercase input.
  38   echo
  39   echo "Roland Evans"
  40   echo "4321 Floppy Dr."
  41   echo "Hardscrabble, CO 80753"
  42   echo "(303) 734-9874"
  43   echo "(303) 734-9892 fax"
  44   echo "revans@zzy.net"
  45   echo "Business partner & old friend"
  46   ;;
  47 
  48   "J" | "j" )
  49   echo
  50   echo "Mildred Jones"
  51   echo "249 E. 7th St., Apt. 19"
  52   echo "New York, NY 10009"
  53   echo "(212) 533-2814"
  54   echo "(212) 533-9972 fax"
  55   echo "milliej@loisaida.com"
  56   echo "Girlfriend"
  57   echo "Birthday: Feb. 11"
  58   ;;
  59 
  60 # Add info for Smith & Zane later.
  61 
  62           * )
  63    # Default option.	  
  64    # Empty input (hitting RETURN) fits here, too.
  65    echo
  66    echo "Not yet in database."
  67   ;;
  68 
  69 esac
  70 
  71 tput sgr0                               # Reset colors to "normal."
  72 
  73 echo
  74 
  75 exit 0


Example 33-12. Drawing a box

   1 #!/bin/bash
   2 # Draw-box.sh: Drawing a box using ASCII characters.
   3 
   4 # Script by Stefano Palmeri, with minor editing by document author.
   5 # Used in the "ABS Guide" with permission.
   6 
   7 
   8 ######################################################################
   9 ###  draw_box function doc  ###
  10 
  11 #  The "draw_box" function lets the user
  12 #+ draw a box into a terminal.       
  13 #
  14 #  Usage: draw_box ROW COLUMN HEIGHT WIDTH [COLOR] 
  15 #  ROW and COLUMN represent the position        
  16 #+ of the upper left angle of the box you're going to draw.
  17 #  ROW and COLUMN must be greater than 0
  18 #+ and less than current terminal dimension.
  19 #  HEIGHT is the number of rows of the box, and must be > 0. 
  20 #  HEIGHT + ROW must be <= than current terminal height. 
  21 #  WIDTH is the number of columns of the box and must be > 0.
  22 #  WIDTH + COLUMN must be <= than current terminal width.
  23 #
  24 # E.g.: If your terminal dimension is 20x80,
  25 #  draw_box 2 3 10 45 is good
  26 #  draw_box 2 3 19 45 has bad HEIGHT value (19+2 > 20)
  27 #  draw_box 2 3 18 78 has bad WIDTH value (78+3 > 80)
  28 #
  29 #  COLOR is the color of the box frame.
  30 #  This is the 5th argument and is optional.
  31 #  0=black 1=red 2=green 3=tan 4=blue 5=purple 6=cyan 7=white.
  32 #  If you pass the function bad arguments,
  33 #+ it will just exit with code 65,
  34 #+ and no messages will be printed on stderr.
  35 #
  36 #  Clear the terminal before you start to draw a box.
  37 #  The clear command is not contained within the function.
  38 #  This allows the user to draw multiple boxes, even overlapping ones.
  39 
  40 ###  end of draw_box function doc  ### 
  41 ######################################################################
  42 
  43 draw_box(){
  44 
  45 #=============#
  46 HORZ="-"
  47 VERT="|"
  48 CORNER_CHAR="+"
  49 
  50 MINARGS=4
  51 E_BADARGS=65
  52 #=============#
  53 
  54 
  55 if [ $# -lt "$MINARGS" ]; then                 # If args are less than 4, exit.
  56     exit $E_BADARGS
  57 fi
  58 
  59 # Looking for non digit chars in arguments.
  60 # Probably it could be done better (exercise for the reader?).
  61 if echo $@ | tr -d [:blank:] | tr -d [:digit:] | grep . &> /dev/null; then
  62    exit $E_BADARGS
  63 fi
  64 
  65 BOX_HEIGHT=`expr $3 - 1`   #  -1 correction needed because angle char "+" is 
  66 BOX_WIDTH=`expr $4 - 1`    #+ a part of both box height and width.
  67 T_ROWS=`tput lines`        #  Define current terminal dimension 
  68 T_COLS=`tput cols`         #+ in rows and columns.
  69          
  70 if [ $1 -lt 1 ] || [ $1 -gt $T_ROWS ]; then    #  Start checking if arguments
  71    exit $E_BADARGS                             #+ are correct.
  72 fi
  73 if [ $2 -lt 1 ] || [ $2 -gt $T_COLS ]; then
  74    exit $E_BADARGS
  75 fi
  76 if [ `expr $1 + $BOX_HEIGHT + 1` -gt $T_ROWS ]; then
  77    exit $E_BADARGS
  78 fi
  79 if [ `expr $2 + $BOX_WIDTH + 1` -gt $T_COLS ]; then
  80    exit $E_BADARGS
  81 fi
  82 if [ $3 -lt 1 ] || [ $4 -lt 1 ]; then
  83    exit $E_BADARGS
  84 fi                                 # End checking arguments.
  85 
  86 plot_char(){                       # Function within a function.
  87    echo -e "\E[${1};${2}H"$3
  88 }
  89 
  90 echo -ne "\E[3${5}m"               # Set box frame color, if defined.
  91 
  92 # start drawing the box
  93 
  94 count=1                                         #  Draw vertical lines using
  95 for (( r=$1; count<=$BOX_HEIGHT; r++)); do      #+ plot_char function.
  96   plot_char $r $2 $VERT
  97   let count=count+1
  98 done 
  99 
 100 count=1
 101 c=`expr $2 + $BOX_WIDTH`
 102 for (( r=$1; count<=$BOX_HEIGHT; r++)); do
 103   plot_char $r $c $VERT
 104   let count=count+1
 105 done 
 106 
 107 count=1                                        #  Draw horizontal lines using
 108 for (( c=$2; count<=$BOX_WIDTH; c++)); do      #+ plot_char function.
 109   plot_char $1 $c $HORZ
 110   let count=count+1
 111 done 
 112 
 113 count=1
 114 r=`expr $1 + $BOX_HEIGHT`
 115 for (( c=$2; count<=$BOX_WIDTH; c++)); do
 116   plot_char $r $c $HORZ
 117   let count=count+1
 118 done 
 119 
 120 plot_char $1 $2 $CORNER_CHAR                   # Draw box angles.
 121 plot_char $1 `expr $2 + $BOX_WIDTH` +
 122 plot_char `expr $1 + $BOX_HEIGHT` $2 +
 123 plot_char `expr $1 + $BOX_HEIGHT` `expr $2 + $BOX_WIDTH` +
 124 
 125 echo -ne "\E[0m"             #  Restore old colors.
 126 
 127 P_ROWS=`expr $T_ROWS - 1`    #  Put the prompt at bottom of the terminal.
 128 
 129 echo -e "\E[${P_ROWS};1H"
 130 }      
 131 
 132 
 133 # Now, let's try drawing a box.
 134 clear                       # Clear the terminal.
 135 R=2      # Row
 136 C=3      # Column
 137 H=10     # Height
 138 W=45     # Width 
 139 col=1    # Color (red)
 140 draw_box $R $C $H $W $col   # Draw the box.
 141 
 142 exit 0
 143 
 144 # Exercise:
 145 # --------
 146 # Add the option of printing text within the drawn box.

The simplest, and perhaps most useful ANSI escape sequence is bold text, \033[1m ... \033[0m. The \033 represents an escape, the "[1" turns on the bold attribute, while the "[0" switches it off. The "m" terminates each term of the escape sequence.
 bash$ echo -e "\033[1mThis is bold text.\033[0m"
 	      

A similar escape sequence switches on the underline attribute (on an rxvt and an aterm).
 bash$ echo -e "\033[4mThis is underlined text.\033[0m"
 	      

Note

With an echo, the -e option enables the escape sequences.

Other escape sequences change the text and/or background color.

 bash$ echo -e '\E[34;47mThis prints in blue.'; tput sgr0
 
 
 bash$ echo -e '\E[33;44m'"yellow text on blue background"; tput sgr0
 
 
 bash$ echo -e '\E[1;33;44m'"BOLD yellow text on blue background"; tput sgr0
 	      

Note

It's usually advisable to set the bold attribute for light-colored foreground text.

The tput sgr0 restores the terminal settings to normal. Omitting this lets all subsequent output from that particular terminal remain blue.

Note

Since tput sgr0 fails to restore terminal settings under certain circumstances, echo -ne \E[0m may be a better choice.

The numbers in the following table work for an rxvt terminal. Results may vary for other terminal emulators.


Table 33-1. Numbers representing colors in Escape Sequences

ColorForegroundBackground
black3040
red3141
green3242
yellow3343
blue3444
magenta3545
cyan3646
white3747


Example 33-13. Echoing colored text

   1 #!/bin/bash
   2 # color-echo.sh: Echoing text messages in color.
   3 
   4 # Modify this script for your own purposes.
   5 # It's easier than hand-coding color.
   6 
   7 black='\E[30;47m'
   8 red='\E[31;47m'
   9 green='\E[32;47m'
  10 yellow='\E[33;47m'
  11 blue='\E[34;47m'
  12 magenta='\E[35;47m'
  13 cyan='\E[36;47m'
  14 white='\E[37;47m'
  15 
  16 
  17 alias Reset="tput sgr0"      #  Reset text attributes to normal
  18                              #+ without clearing screen.
  19 
  20 
  21 cecho ()                     # Color-echo.
  22                              # Argument $1 = message
  23                              # Argument $2 = color
  24 {
  25 local default_msg="No message passed."
  26                              # Doesn't really need to be a local variable.
  27 
  28 message=${1:-$default_msg}   # Defaults to default message.
  29 color=${2:-$black}           # Defaults to black, if not specified.
  30 
  31   echo -e "$color"
  32   echo "$message"
  33   Reset                      # Reset to normal.
  34 
  35   return
  36 }  
  37 
  38 
  39 # Now, let's try it out.
  40 # ----------------------------------------------------
  41 cecho "Feeling blue..." $blue
  42 cecho "Magenta looks more like purple." $magenta
  43 cecho "Green with envy." $green
  44 cecho "Seeing red?" $red
  45 cecho "Cyan, more familiarly known as aqua." $cyan
  46 cecho "No color passed (defaults to black)."
  47        # Missing $color argument.
  48 cecho "\"Empty\" color passed (defaults to black)." ""
  49        # Empty $color argument.
  50 cecho
  51        # Missing $message and $color arguments.
  52 cecho "" ""
  53        # Empty $message and $color arguments.
  54 # ----------------------------------------------------
  55 
  56 echo
  57 
  58 exit 0
  59 
  60 # Exercises:
  61 # ---------
  62 # 1) Add the "bold" attribute to the 'cecho ()' function.
  63 # 2) Add options for colored backgrounds.


Example 33-14. A "horserace" game

   1 #!/bin/bash
   2 # horserace.sh: very simple horserace simulation.
   3 # Author: Stefano Palmeri
   4 # Used with permission.
   5 
   6 ################################################################
   7 #  Goals of the script:
   8 #  playing with escape sequences and terminal colors.
   9 #
  10 #  Exercise:
  11 #  Edit the script to make it run less randomly,
  12 #+ set up a fake betting shop . . .     
  13 #  Um . . . um . . . it's starting to remind me of a movie . . .
  14 #
  15 #  The script gives each horse a random handicap.
  16 #  The odds are calculated upon horse handicap
  17 #+ and are expressed in European(?) style.
  18 #  E.g.: odds=3.75 means that if you bet $1 and win,
  19 #+ you receive $3.75.
  20 # 
  21 #  The script has been tested with a GNU/Linux OS,
  22 #+ using xterm and rxvt, and konsole.
  23 #  On a machine with an AMD 900 MHz processor,
  24 #+ the average race time is 75 seconds.    
  25 #  On faster computers the race time would be lower.
  26 #  So, if you want more suspense, reset the USLEEP_ARG variable.
  27 #
  28 #  Script by Stefano Palmeri.
  29 ################################################################
  30 
  31 E_RUNERR=65
  32 
  33 # Check if md5sum and bc are installed. 
  34 if ! which bc &> /dev/null; then
  35    echo bc is not installed.  
  36    echo "Can\'t run . . . "
  37    exit $E_RUNERR
  38 fi
  39 if ! which md5sum &> /dev/null; then
  40    echo md5sum is not installed.  
  41    echo "Can\'t run . . . "
  42    exit $E_RUNERR
  43 fi
  44 
  45 #  Set the following variable to slow down script execution.
  46 #  It will be passed as the argument for usleep (man usleep)  
  47 #+ and is expressed in microseconds (500000 = half a second).
  48 USLEEP_ARG=0  
  49 
  50 #  Clean up the temp directory, restore terminal cursor and 
  51 #+ terminal colors -- if script interrupted by Ctl-C.
  52 trap 'echo -en "\E[?25h"; echo -en "\E[0m"; stty echo;\
  53 tput cup 20 0; rm -fr  $HORSE_RACE_TMP_DIR'  TERM EXIT
  54 #  See the chapter on debugging for an explanation of 'trap.'
  55 
  56 # Set a unique (paranoid) name for the temp directory the script needs.
  57 HORSE_RACE_TMP_DIR=$HOME/.horserace-`date +%s`-`head -c10 /dev/urandom | md5sum | head -c30`
  58 
  59 # Create the temp directory and move right in.
  60 mkdir $HORSE_RACE_TMP_DIR
  61 cd $HORSE_RACE_TMP_DIR
  62 
  63 
  64 #  This function moves the cursor to line $1 column $2 and then prints $3.
  65 #  E.g.: "move_and_echo 5 10 linux" is equivalent to
  66 #+ "tput cup 4 9; echo linux", but with one command instead of two.
  67 #  Note: "tput cup" defines 0 0 the upper left angle of the terminal,
  68 #+ echo defines 1 1 the upper left angle of the terminal.
  69 move_and_echo() {
  70           echo -ne "\E[${1};${2}H""$3" 
  71 }
  72 
  73 # Function to generate a pseudo-random number between 1 and 9. 
  74 random_1_9 () {
  75                 head -c10 /dev/urandom | md5sum | tr -d [a-z] | tr -d 0 | cut -c1 
  76 }
  77 
  78 #  Two functions that simulate "movement," when drawing the horses. 
  79 draw_horse_one() {
  80                echo -n " "//$MOVE_HORSE//
  81 }
  82 draw_horse_two(){
  83               echo -n " "\\\\$MOVE_HORSE\\\\ 
  84 }   
  85 
  86 
  87 # Define current terminal dimension.
  88 N_COLS=`tput cols`
  89 N_LINES=`tput lines`
  90 
  91 # Need at least a 20-LINES X 80-COLUMNS terminal. Check it.
  92 if [ $N_COLS -lt 80 ] || [ $N_LINES -lt 20 ]; then
  93    echo "`basename $0` needs a 80-cols X 20-lines terminal."
  94    echo "Your terminal is ${N_COLS}-cols X ${N_LINES}-lines."
  95    exit $E_RUNERR
  96 fi
  97 
  98 
  99 # Start drawing the race field.
 100 
 101 # Need a string of 80 chars. See below.
 102 BLANK80=`seq -s "" 100 | head -c80`
 103 
 104 clear
 105 
 106 # Set foreground and background colors to white.
 107 echo -ne '\E[37;47m'
 108 
 109 # Move the cursor on the upper left angle of the terminal.
 110 tput cup 0 0 
 111 
 112 # Draw six white lines.
 113 for n in `seq 5`; do
 114       echo $BLANK80        # Use the 80 chars string to colorize the terminal.  
 115 done
 116 
 117 # Sets foreground color to black. 
 118 echo -ne '\E[30m'
 119 
 120 move_and_echo 3 1 "START  1"            
 121 move_and_echo 3 75 FINISH
 122 move_and_echo 1 5 "|"
 123 move_and_echo 1 80 "|"
 124 move_and_echo 2 5 "|"
 125 move_and_echo 2 80 "|"
 126 move_and_echo 4 5 "|  2"
 127 move_and_echo 4 80 "|"
 128 move_and_echo 5 5 "V  3"
 129 move_and_echo 5 80 "V"
 130 
 131 # Set foreground color to red. 
 132 echo -ne '\E[31m'
 133 
 134 # Some ASCII art.
 135 move_and_echo 1 8 "..@@@..@@@@@...@@@@@.@...@..@@@@..."
 136 move_and_echo 2 8 ".@...@...@.......@...@...@.@......."
 137 move_and_echo 3 8 ".@@@@@...@.......@...@@@@@.@@@@...."
 138 move_and_echo 4 8 ".@...@...@.......@...@...@.@......."
 139 move_and_echo 5 8 ".@...@...@.......@...@...@..@@@@..."
 140 move_and_echo 1 43 "@@@@...@@@...@@@@..@@@@..@@@@."
 141 move_and_echo 2 43 "@...@.@...@.@.....@.....@....."
 142 move_and_echo 3 43 "@@@@..@@@@@.@.....@@@@...@@@.."
 143 move_and_echo 4 43 "@..@..@...@.@.....@.........@."
 144 move_and_echo 5 43 "@...@.@...@..@@@@..@@@@.@@@@.."
 145 
 146 
 147 # Set foreground and background colors to green.
 148 echo -ne '\E[32;42m'
 149 
 150 # Draw  eleven green lines.
 151 tput cup 5 0
 152 for n in `seq 11`; do
 153       echo $BLANK80
 154 done
 155 
 156 # Set foreground color to black. 
 157 echo -ne '\E[30m'
 158 tput cup 5 0
 159 
 160 # Draw the fences. 
 161 echo "++++++++++++++++++++++++++++++++++++++\
 162 ++++++++++++++++++++++++++++++++++++++++++"
 163 
 164 tput cup 15 0
 165 echo "++++++++++++++++++++++++++++++++++++++\
 166 ++++++++++++++++++++++++++++++++++++++++++"
 167 
 168 # Set foreground and background colors to white.
 169 echo -ne '\E[37;47m'
 170 
 171 # Draw three white lines.
 172 for n in `seq 3`; do
 173       echo $BLANK80
 174 done
 175 
 176 # Set foreground color to black.
 177 echo -ne '\E[30m'
 178 
 179 # Create 9 files to stores handicaps.
 180 for n in `seq 10 7 68`; do
 181       touch $n
 182 done  
 183 
 184 # Set the first type of "horse" the script will draw.
 185 HORSE_TYPE=2
 186 
 187 #  Create position-file and odds-file for every "horse".
 188 #+ In these files, store the current position of the horse,
 189 #+ the type and the odds.
 190 for HN in `seq 9`; do
 191       touch horse_${HN}_position
 192       touch odds_${HN}
 193       echo \-1 > horse_${HN}_position
 194       echo $HORSE_TYPE >>  horse_${HN}_position
 195       # Define a random handicap for horse.
 196        HANDICAP=`random_1_9`
 197       # Check if the random_1_9 function returned a good value.
 198       while ! echo $HANDICAP | grep [1-9] &> /dev/null; do
 199                 HANDICAP=`random_1_9`
 200       done
 201       # Define last handicap position for horse. 
 202       LHP=`expr $HANDICAP \* 7 + 3`
 203       for FILE in `seq 10 7 $LHP`; do
 204             echo $HN >> $FILE
 205       done   
 206      
 207       # Calculate odds.
 208       case $HANDICAP in 
 209               1) ODDS=`echo $HANDICAP \* 0.25 + 1.25 | bc`
 210                                  echo $ODDS > odds_${HN}
 211               ;;
 212               2 | 3) ODDS=`echo $HANDICAP \* 0.40 + 1.25 | bc`
 213                                        echo $ODDS > odds_${HN}
 214               ;;
 215               4 | 5 | 6) ODDS=`echo $HANDICAP \* 0.55 + 1.25 | bc`
 216                                              echo $ODDS > odds_${HN}
 217               ;; 
 218               7 | 8) ODDS=`echo $HANDICAP \* 0.75 + 1.25 | bc`
 219                                        echo $ODDS > odds_${HN}
 220               ;; 
 221               9) ODDS=`echo $HANDICAP \* 0.90 + 1.25 | bc`
 222                                   echo $ODDS > odds_${HN}
 223       esac
 224 
 225 
 226 done
 227 
 228 
 229 # Print odds.
 230 print_odds() {
 231 tput cup 6 0
 232 echo -ne '\E[30;42m'
 233 for HN in `seq 9`; do
 234       echo "#$HN odds->" `cat odds_${HN}`
 235 done
 236 }
 237 
 238 # Draw the horses at starting line.
 239 draw_horses() {
 240 tput cup 6 0
 241 echo -ne '\E[30;42m'
 242 for HN in `seq 9`; do
 243       echo /\\$HN/\\"                               "
 244 done
 245 }
 246 
 247 print_odds
 248 
 249 echo -ne '\E[47m'
 250 # Wait for a enter key press to start the race.
 251 # The escape sequence '\E[?25l' disables the cursor.
 252 tput cup 17 0
 253 echo -e '\E[?25l'Press [enter] key to start the race...
 254 read -s
 255 
 256 #  Disable normal echoing in the terminal.
 257 #  This avoids key presses that might "contaminate" the screen
 258 #+ during the race.  
 259 stty -echo
 260 
 261 # --------------------------------------------------------
 262 # Start the race.
 263 
 264 draw_horses
 265 echo -ne '\E[37;47m'
 266 move_and_echo 18 1 $BLANK80
 267 echo -ne '\E[30m'
 268 move_and_echo 18 1 Starting...
 269 sleep 1
 270 
 271 # Set the column of the finish line.
 272 WINNING_POS=74
 273 
 274 # Define the time the race started.
 275 START_TIME=`date +%s`
 276 
 277 # COL variable needed by following "while" construct.
 278 COL=0    
 279 
 280 while [ $COL -lt $WINNING_POS ]; do
 281                    
 282           MOVE_HORSE=0     
 283           
 284           # Check if the random_1_9 function has returned a good value.
 285           while ! echo $MOVE_HORSE | grep [1-9] &> /dev/null; do
 286                 MOVE_HORSE=`random_1_9`
 287           done
 288           
 289           # Define old type and position of the "randomized horse".
 290           HORSE_TYPE=`cat  horse_${MOVE_HORSE}_position | tail -n 1`
 291           COL=$(expr `cat  horse_${MOVE_HORSE}_position | head -n 1`) 
 292           
 293           ADD_POS=1
 294           # Check if the current position is an handicap position. 
 295           if seq 10 7 68 | grep -w $COL &> /dev/null; then
 296                 if grep -w $MOVE_HORSE $COL &> /dev/null; then
 297                       ADD_POS=0
 298                       grep -v -w  $MOVE_HORSE $COL > ${COL}_new
 299                       rm -f $COL
 300                       mv -f ${COL}_new $COL
 301                       else ADD_POS=1
 302                 fi 
 303           else ADD_POS=1
 304           fi
 305           COL=`expr $COL + $ADD_POS`
 306           echo $COL >  horse_${MOVE_HORSE}_position  # Store new position.
 307                             
 308          # Choose the type of horse to draw.         
 309           case $HORSE_TYPE in 
 310                 1) HORSE_TYPE=2; DRAW_HORSE=draw_horse_two
 311                 ;;
 312                 2) HORSE_TYPE=1; DRAW_HORSE=draw_horse_one 
 313           esac       
 314           echo $HORSE_TYPE >>  horse_${MOVE_HORSE}_position # Store current type.
 315          
 316           # Set foreground color to black and background to green.
 317           echo -ne '\E[30;42m'
 318           
 319           # Move the cursor to new horse position.
 320           tput cup `expr $MOVE_HORSE + 5`  `cat  horse_${MOVE_HORSE}_position | head -n 1` 
 321           
 322           # Draw the horse.
 323           $DRAW_HORSE
 324            usleep $USLEEP_ARG
 325           
 326            # When all horses have gone beyond field line 15, reprint odds.          
 327            touch fieldline15
 328            if [ $COL = 15 ]; then
 329              echo $MOVE_HORSE >> fieldline15  
 330            fi
 331            if [ `wc -l fieldline15 | cut -f1 -d " "` = 9 ]; then
 332                print_odds
 333                : > fieldline15
 334            fi           
 335           
 336           # Define the leading horse.
 337           HIGHEST_POS=`cat *position | sort -n | tail -1`          
 338           
 339           # Set background color to white.
 340           echo -ne '\E[47m'
 341           tput cup 17 0
 342           echo -n Current leader: `grep -w $HIGHEST_POS *position | cut -c7`"                              "           
 343 
 344 done  
 345 
 346 # Define the time the race finished.
 347 FINISH_TIME=`date +%s`
 348 
 349 # Set background color to green and enable blinking text.
 350 echo -ne '\E[30;42m'
 351 echo -en '\E[5m'
 352 
 353 # Make the winning horse blink.
 354 tput cup `expr $MOVE_HORSE + 5` `cat  horse_${MOVE_HORSE}_position | head -n 1`
 355 $DRAW_HORSE
 356 
 357 # Disable blinking text.
 358 echo -en '\E[25m'
 359 
 360 # Set foreground and background color to white.
 361 echo -ne '\E[37;47m'
 362 move_and_echo 18 1 $BLANK80
 363 
 364 # Set foreground color to black.
 365 echo -ne '\E[30m'
 366 
 367 # Make winner blink.
 368 tput cup 17 0
 369 echo -e "\E[5mWINNER: $MOVE_HORSE\E[25m""  Odds: `cat odds_${MOVE_HORSE}`"\
 370 "  Race time: `expr $FINISH_TIME - $START_TIME` secs"
 371 
 372 # Restore cursor and old colors.
 373 echo -en "\E[?25h"
 374 echo -en "\E[0m"
 375 
 376 # Restore echoing.
 377 stty echo
 378 
 379 # Remove race temp directory.
 380 rm -rf $HORSE_RACE_TMP_DIR
 381 
 382 tput cup 19 0
 383 
 384 exit 0

See also Example A-22.

Caution

There is, however, a major problem with all this. ANSI escape sequences are emphatically non-portable. What works fine on some terminal emulators (or the console) may work differently, or not at all, on others. A "colorized" script that looks stunning on the script author's machine may produce unreadable output on someone else's. This greatly compromises the usefulness of "colorizing" scripts, and possibly relegates this technique to the status of a gimmick or even a "toy".

Moshe Jacobson's color utility (http://runslinux.net/projects.html#color) considerably simplifies using ANSI escape sequences. It substitutes a clean and logical syntax for the clumsy constructs just discussed.

Henry/teikedvl has likewise created a utility (http://scriptechocolor.sourceforge.net/) to simplify creation of colorized scripts.

Notes

[1]

ANSI is, of course, the acronym for the American National Standards Institute. This august body establishes and maintains various technical and industrial standards.