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 # Minor edits suggested by Jim Angstadt.
   6 # Used in the "ABS Guide" with permission.
   7 
   8 
   9 ######################################################################
  10 ###  draw_box function doc  ###
  11 
  12 #  The "draw_box" function lets the user
  13 #+ draw a box into a terminal.       
  14 #
  15 #  Usage: draw_box ROW COLUMN HEIGHT WIDTH [COLOR] 
  16 #  ROW and COLUMN represent the position        
  17 #+ of the upper left angle of the box you're going to draw.
  18 #  ROW and COLUMN must be greater than 0
  19 #+ and less than current terminal dimension.
  20 #  HEIGHT is the number of rows of the box, and must be > 0. 
  21 #  HEIGHT + ROW must be <= than current terminal height. 
  22 #  WIDTH is the number of columns of the box and must be > 0.
  23 #  WIDTH + COLUMN must be <= than current terminal width.
  24 #
  25 # E.g.: If your terminal dimension is 20x80,
  26 #  draw_box 2 3 10 45 is good
  27 #  draw_box 2 3 19 45 has bad HEIGHT value (19+2 > 20)
  28 #  draw_box 2 3 18 78 has bad WIDTH value (78+3 > 80)
  29 #
  30 #  COLOR is the color of the box frame.
  31 #  This is the 5th argument and is optional.
  32 #  0=black 1=red 2=green 3=tan 4=blue 5=purple 6=cyan 7=white.
  33 #  If you pass the function bad arguments,
  34 #+ it will just exit with code 65,
  35 #+ and no messages will be printed on stderr.
  36 #
  37 #  Clear the terminal before you start to draw a box.
  38 #  The clear command is not contained within the function.
  39 #  This allows the user to draw multiple boxes, even overlapping ones.
  40 
  41 ###  end of draw_box function doc  ### 
  42 ######################################################################
  43 
  44 draw_box(){
  45 
  46 #=============#
  47 HORZ="-"
  48 VERT="|"
  49 CORNER_CHAR="+"
  50 
  51 MINARGS=4
  52 E_BADARGS=65
  53 #=============#
  54 
  55 
  56 if [ $# -lt "$MINARGS" ]; then                 # If args are less than 4, exit.
  57     exit $E_BADARGS
  58 fi
  59 
  60 # Looking for non digit chars in arguments.
  61 # Probably it could be done better (exercise for the reader?).
  62 if echo $@ | tr -d [:blank:] | tr -d [:digit:] | grep . &> /dev/null; then
  63    exit $E_BADARGS
  64 fi
  65 
  66 BOX_HEIGHT=`expr $3 - 1`   #  -1 correction needed because angle char "+" is 
  67 BOX_WIDTH=`expr $4 - 1`    #+ a part of both box height and width.
  68 T_ROWS=`tput lines`        #  Define current terminal dimension 
  69 T_COLS=`tput cols`         #+ in rows and columns.
  70          
  71 if [ $1 -lt 1 ] || [ $1 -gt $T_ROWS ]; then    #  Start checking if arguments
  72    exit $E_BADARGS                             #+ are correct.
  73 fi
  74 if [ $2 -lt 1 ] || [ $2 -gt $T_COLS ]; then
  75    exit $E_BADARGS
  76 fi
  77 if [ `expr $1 + $BOX_HEIGHT + 1` -gt $T_ROWS ]; then
  78    exit $E_BADARGS
  79 fi
  80 if [ `expr $2 + $BOX_WIDTH + 1` -gt $T_COLS ]; then
  81    exit $E_BADARGS
  82 fi
  83 if [ $3 -lt 1 ] || [ $4 -lt 1 ]; then
  84    exit $E_BADARGS
  85 fi                                 # End checking arguments.
  86 
  87 plot_char(){                       # Function within a function.
  88    echo -e "\E[${1};${2}H"$3
  89 }
  90 
  91 echo -ne "\E[3${5}m"               # Set box frame color, if defined.
  92 
  93 # start drawing the box
  94 
  95 count=1                                         #  Draw vertical lines using
  96 for (( r=$1; count<=$BOX_HEIGHT; r++)); do      #+ plot_char function.
  97   plot_char $r $2 $VERT
  98   let count=count+1
  99 done 
 100 
 101 count=1
 102 c=`expr $2 + $BOX_WIDTH`
 103 for (( r=$1; count<=$BOX_HEIGHT; r++)); do
 104   plot_char $r $c $VERT
 105   let count=count+1
 106 done 
 107 
 108 count=1                                        #  Draw horizontal lines using
 109 for (( c=$2; count<=$BOX_WIDTH; c++)); do      #+ plot_char function.
 110   plot_char $1 $c $HORZ
 111   let count=count+1
 112 done 
 113 
 114 count=1
 115 r=`expr $1 + $BOX_HEIGHT`
 116 for (( c=$2; count<=$BOX_WIDTH; c++)); do
 117   plot_char $r $c $HORZ
 118   let count=count+1
 119 done 
 120 
 121 plot_char $1 $2 $CORNER_CHAR                   # Draw box angles.
 122 plot_char $1 `expr $2 + $BOX_WIDTH` $CORNER_CHAR
 123 plot_char `expr $1 + $BOX_HEIGHT` $2 $CORNER_CHAR
 124 plot_char `expr $1 + $BOX_HEIGHT` `expr $2 + $BOX_WIDTH` $CORNER_CHAR
 125 
 126 echo -ne "\E[0m"             #  Restore old colors.
 127 
 128 P_ROWS=`expr $T_ROWS - 1`    #  Put the prompt at bottom of the terminal.
 129 
 130 echo -e "\E[${P_ROWS};1H"
 131 }      
 132 
 133 
 134 # Now, let's try drawing a box.
 135 clear                       # Clear the terminal.
 136 R=2      # Row
 137 C=3      # Column
 138 H=10     # Height
 139 W=45     # Width 
 140 col=1    # Color (red)
 141 draw_box $R $C $H $W $col   # Draw the box.
 142 
 143 exit 0
 144 
 145 # Exercise:
 146 # --------
 147 # 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 \
  58 | md5sum | head -c30`
  59 
  60 # Create the temp directory and move right in.
  61 mkdir $HORSE_RACE_TMP_DIR
  62 cd $HORSE_RACE_TMP_DIR
  63 
  64 
  65 #  This function moves the cursor to line $1 column $2 and then prints $3.
  66 #  E.g.: "move_and_echo 5 10 linux" is equivalent to
  67 #+ "tput cup 4 9; echo linux", but with one command instead of two.
  68 #  Note: "tput cup" defines 0 0 the upper left angle of the terminal,
  69 #+ echo defines 1 1 the upper left angle of the terminal.
  70 move_and_echo() {
  71           echo -ne "\E[${1};${2}H""$3" 
  72 }
  73 
  74 # Function to generate a pseudo-random number between 1 and 9. 
  75 random_1_9 ()
  76 {
  77     head -c10 /dev/urandom | md5sum | tr -d [a-z] | tr -d 0 | cut -c1 
  78 }
  79 
  80 #  Two functions that simulate "movement," when drawing the horses. 
  81 draw_horse_one() {
  82                echo -n " "//$MOVE_HORSE//
  83 }
  84 draw_horse_two(){
  85               echo -n " "\\\\$MOVE_HORSE\\\\ 
  86 }   
  87 
  88 
  89 # Define current terminal dimension.
  90 N_COLS=`tput cols`
  91 N_LINES=`tput lines`
  92 
  93 # Need at least a 20-LINES X 80-COLUMNS terminal. Check it.
  94 if [ $N_COLS -lt 80 ] || [ $N_LINES -lt 20 ]; then
  95    echo "`basename $0` needs a 80-cols X 20-lines terminal."
  96    echo "Your terminal is ${N_COLS}-cols X ${N_LINES}-lines."
  97    exit $E_RUNERR
  98 fi
  99 
 100 
 101 # Start drawing the race field.
 102 
 103 # Need a string of 80 chars. See below.
 104 BLANK80=`seq -s "" 100 | head -c80`
 105 
 106 clear
 107 
 108 # Set foreground and background colors to white.
 109 echo -ne '\E[37;47m'
 110 
 111 # Move the cursor on the upper left angle of the terminal.
 112 tput cup 0 0 
 113 
 114 # Draw six white lines.
 115 for n in `seq 5`; do
 116       echo $BLANK80   # Use the 80 chars string to colorize the terminal.
 117 done
 118 
 119 # Sets foreground color to black. 
 120 echo -ne '\E[30m'
 121 
 122 move_and_echo 3 1 "START  1"            
 123 move_and_echo 3 75 FINISH
 124 move_and_echo 1 5 "|"
 125 move_and_echo 1 80 "|"
 126 move_and_echo 2 5 "|"
 127 move_and_echo 2 80 "|"
 128 move_and_echo 4 5 "|  2"
 129 move_and_echo 4 80 "|"
 130 move_and_echo 5 5 "V  3"
 131 move_and_echo 5 80 "V"
 132 
 133 # Set foreground color to red. 
 134 echo -ne '\E[31m'
 135 
 136 # Some ASCII art.
 137 move_and_echo 1 8 "..@@@..@@@@@...@@@@@.@...@..@@@@..."
 138 move_and_echo 2 8 ".@...@...@.......@...@...@.@......."
 139 move_and_echo 3 8 ".@@@@@...@.......@...@@@@@.@@@@...."
 140 move_and_echo 4 8 ".@...@...@.......@...@...@.@......."
 141 move_and_echo 5 8 ".@...@...@.......@...@...@..@@@@..."
 142 move_and_echo 1 43 "@@@@...@@@...@@@@..@@@@..@@@@."
 143 move_and_echo 2 43 "@...@.@...@.@.....@.....@....."
 144 move_and_echo 3 43 "@@@@..@@@@@.@.....@@@@...@@@.."
 145 move_and_echo 4 43 "@..@..@...@.@.....@.........@."
 146 move_and_echo 5 43 "@...@.@...@..@@@@..@@@@.@@@@.."
 147 
 148 
 149 # Set foreground and background colors to green.
 150 echo -ne '\E[32;42m'
 151 
 152 # Draw  eleven green lines.
 153 tput cup 5 0
 154 for n in `seq 11`; do
 155       echo $BLANK80
 156 done
 157 
 158 # Set foreground color to black. 
 159 echo -ne '\E[30m'
 160 tput cup 5 0
 161 
 162 # Draw the fences. 
 163 echo "++++++++++++++++++++++++++++++++++++++\
 164 ++++++++++++++++++++++++++++++++++++++++++"
 165 
 166 tput cup 15 0
 167 echo "++++++++++++++++++++++++++++++++++++++\
 168 ++++++++++++++++++++++++++++++++++++++++++"
 169 
 170 # Set foreground and background colors to white.
 171 echo -ne '\E[37;47m'
 172 
 173 # Draw three white lines.
 174 for n in `seq 3`; do
 175       echo $BLANK80
 176 done
 177 
 178 # Set foreground color to black.
 179 echo -ne '\E[30m'
 180 
 181 # Create 9 files to stores handicaps.
 182 for n in `seq 10 7 68`; do
 183       touch $n
 184 done  
 185 
 186 # Set the first type of "horse" the script will draw.
 187 HORSE_TYPE=2
 188 
 189 #  Create position-file and odds-file for every "horse".
 190 #+ In these files, store the current position of the horse,
 191 #+ the type and the odds.
 192 for HN in `seq 9`; do
 193       touch horse_${HN}_position
 194       touch odds_${HN}
 195       echo \-1 > horse_${HN}_position
 196       echo $HORSE_TYPE >>  horse_${HN}_position
 197       # Define a random handicap for horse.
 198        HANDICAP=`random_1_9`
 199       # Check if the random_1_9 function returned a good value.
 200       while ! echo $HANDICAP | grep [1-9] &> /dev/null; do
 201                 HANDICAP=`random_1_9`
 202       done
 203       # Define last handicap position for horse. 
 204       LHP=`expr $HANDICAP \* 7 + 3`
 205       for FILE in `seq 10 7 $LHP`; do
 206             echo $HN >> $FILE
 207       done   
 208      
 209       # Calculate odds.
 210       case $HANDICAP in 
 211               1) ODDS=`echo $HANDICAP \* 0.25 + 1.25 | bc`
 212                                  echo $ODDS > odds_${HN}
 213               ;;
 214               2 | 3) ODDS=`echo $HANDICAP \* 0.40 + 1.25 | bc`
 215                                        echo $ODDS > odds_${HN}
 216               ;;
 217               4 | 5 | 6) ODDS=`echo $HANDICAP \* 0.55 + 1.25 | bc`
 218                                              echo $ODDS > odds_${HN}
 219               ;; 
 220               7 | 8) ODDS=`echo $HANDICAP \* 0.75 + 1.25 | bc`
 221                                        echo $ODDS > odds_${HN}
 222               ;; 
 223               9) ODDS=`echo $HANDICAP \* 0.90 + 1.25 | bc`
 224                                   echo $ODDS > odds_${HN}
 225       esac
 226 
 227 
 228 done
 229 
 230 
 231 # Print odds.
 232 print_odds() {
 233 tput cup 6 0
 234 echo -ne '\E[30;42m'
 235 for HN in `seq 9`; do
 236       echo "#$HN odds->" `cat odds_${HN}`
 237 done
 238 }
 239 
 240 # Draw the horses at starting line.
 241 draw_horses() {
 242 tput cup 6 0
 243 echo -ne '\E[30;42m'
 244 for HN in `seq 9`; do
 245       echo /\\$HN/\\"                               "
 246 done
 247 }
 248 
 249 print_odds
 250 
 251 echo -ne '\E[47m'
 252 # Wait for a enter key press to start the race.
 253 # The escape sequence '\E[?25l' disables the cursor.
 254 tput cup 17 0
 255 echo -e '\E[?25l'Press [enter] key to start the race...
 256 read -s
 257 
 258 #  Disable normal echoing in the terminal.
 259 #  This avoids key presses that might "contaminate" the screen
 260 #+ during the race.  
 261 stty -echo
 262 
 263 # --------------------------------------------------------
 264 # Start the race.
 265 
 266 draw_horses
 267 echo -ne '\E[37;47m'
 268 move_and_echo 18 1 $BLANK80
 269 echo -ne '\E[30m'
 270 move_and_echo 18 1 Starting...
 271 sleep 1
 272 
 273 # Set the column of the finish line.
 274 WINNING_POS=74
 275 
 276 # Define the time the race started.
 277 START_TIME=`date +%s`
 278 
 279 # COL variable needed by following "while" construct.
 280 COL=0    
 281 
 282 while [ $COL -lt $WINNING_POS ]; do
 283                    
 284           MOVE_HORSE=0     
 285           
 286           # Check if the random_1_9 function has returned a good value.
 287           while ! echo $MOVE_HORSE | grep [1-9] &> /dev/null; do
 288                 MOVE_HORSE=`random_1_9`
 289           done
 290           
 291           # Define old type and position of the "randomized horse".
 292           HORSE_TYPE=`cat  horse_${MOVE_HORSE}_position | tail -n 1`
 293           COL=$(expr `cat  horse_${MOVE_HORSE}_position | head -n 1`)
 294           
 295           ADD_POS=1
 296           # Check if the current position is an handicap position. 
 297           if seq 10 7 68 | grep -w $COL &> /dev/null; then
 298                 if grep -w $MOVE_HORSE $COL &> /dev/null; then
 299                       ADD_POS=0
 300                       grep -v -w  $MOVE_HORSE $COL > ${COL}_new
 301                       rm -f $COL
 302                       mv -f ${COL}_new $COL
 303                       else ADD_POS=1
 304                 fi 
 305           else ADD_POS=1
 306           fi
 307           COL=`expr $COL + $ADD_POS`
 308           echo $COL >  horse_${MOVE_HORSE}_position  # Store new position.
 309                             
 310          # Choose the type of horse to draw.         
 311           case $HORSE_TYPE in 
 312                 1) HORSE_TYPE=2; DRAW_HORSE=draw_horse_two
 313                 ;;
 314                 2) HORSE_TYPE=1; DRAW_HORSE=draw_horse_one 
 315           esac       
 316           echo $HORSE_TYPE >>  horse_${MOVE_HORSE}_position
 317           # Store current type.
 318          
 319           # Set foreground color to black and background to green.
 320           echo -ne '\E[30;42m'
 321           
 322           # Move the cursor to new horse position.
 323           tput cup `expr $MOVE_HORSE + 5` \
 324 	  `cat  horse_${MOVE_HORSE}_position | head -n 1` 
 325           
 326           # Draw the horse.
 327           $DRAW_HORSE
 328            usleep $USLEEP_ARG
 329           
 330            # When all horses have gone beyond field line 15, reprint odds.
 331            touch fieldline15
 332            if [ $COL = 15 ]; then
 333              echo $MOVE_HORSE >> fieldline15  
 334            fi
 335            if [ `wc -l fieldline15 | cut -f1 -d " "` = 9 ]; then
 336                print_odds
 337                : > fieldline15
 338            fi           
 339           
 340           # Define the leading horse.
 341           HIGHEST_POS=`cat *position | sort -n | tail -1`          
 342           
 343           # Set background color to white.
 344           echo -ne '\E[47m'
 345           tput cup 17 0
 346           echo -n Current leader: `grep -w $HIGHEST_POS *position | cut -c7`\
 347 	  "                              "
 348 
 349 done  
 350 
 351 # Define the time the race finished.
 352 FINISH_TIME=`date +%s`
 353 
 354 # Set background color to green and enable blinking text.
 355 echo -ne '\E[30;42m'
 356 echo -en '\E[5m'
 357 
 358 # Make the winning horse blink.
 359 tput cup `expr $MOVE_HORSE + 5` \
 360 `cat  horse_${MOVE_HORSE}_position | head -n 1`
 361 $DRAW_HORSE
 362 
 363 # Disable blinking text.
 364 echo -en '\E[25m'
 365 
 366 # Set foreground and background color to white.
 367 echo -ne '\E[37;47m'
 368 move_and_echo 18 1 $BLANK80
 369 
 370 # Set foreground color to black.
 371 echo -ne '\E[30m'
 372 
 373 # Make winner blink.
 374 tput cup 17 0
 375 echo -e "\E[5mWINNER: $MOVE_HORSE\E[25m""  Odds: `cat odds_${MOVE_HORSE}`"\
 376 "  Race time: `expr $FINISH_TIME - $START_TIME` secs"
 377 
 378 # Restore cursor and old colors.
 379 echo -en "\E[?25h"
 380 echo -en "\E[0m"
 381 
 382 # Restore echoing.
 383 stty echo
 384 
 385 # Remove race temp directory.
 386 rm -rf $HORSE_RACE_TMP_DIR
 387 
 388 tput cup 19 0
 389 
 390 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.