33.5. 彩色脚本

ANSI [1] 定义了屏幕属性的转义序列集合,例如粗体文本,背景和前景颜色. DOS批处理文件(batch files) 一般使用ANSI的转义代码来控制色彩输出,Bash脚本也是这么做的.


例子 33-11. 一个 "彩色的" 地址资料库

   1 #!/bin/bash
   2 # ex30a.sh: ex30.sh的"彩色" 版本.
   3 #            没有加工处理的地址资料库
   4 
   5 
   6 clear                                   # 清除屏幕.
   7 
   8 echo -n "          "
   9 echo -e '\E[37;44m'"\033[1mContact List\033[0m"
  10                                         # 白色为前景色,蓝色为背景色
  11 echo; echo
  12 echo -e "\033[1mChoose one of the following persons:\033[0m"
  13                                         # 粗体
  14 tput sgr0
  15 echo "(Enter only the first letter of name.)"
  16 echo
  17 echo -en '\E[47;34m'"\033[1mE\033[0m"   # 蓝色
  18 tput sgr0                               # 把色彩设置为"常规"
  19 echo "vans, Roland"                     # "[E]vans, Roland"
  20 echo -en '\E[47;35m'"\033[1mJ\033[0m"   # 红紫色
  21 tput sgr0
  22 echo "ones, Mildred"
  23 echo -en '\E[47;32m'"\033[1mS\033[0m"   # 绿色
  24 tput sgr0
  25 echo "mith, Julie"
  26 echo -en '\E[47;31m'"\033[1mZ\033[0m"   # 红色
  27 tput sgr0
  28 echo "ane, Morris"
  29 echo
  30 
  31 read person
  32 
  33 case "$person" in
  34 # 注意变量被引起来了.
  35 
  36   "E" | "e" )
  37   # 接受大小写的输入.
  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 # 稍后为 Smith 和 Zane 增加信息.
  61 
  62           * )
  63    # 默认选项Default option.	  
  64    # 空的输入(直接按了回车) 也会匹配这儿.
  65    echo
  66    echo "Not yet in database."
  67   ;;
  68 
  69 esac
  70 
  71 tput sgr0                               # 把色彩重设为"常规".
  72 
  73 echo
  74 
  75 exit 0


例子 33-12. 画盒子

   1 #!/bin/bash
   2 # Draw-box.sh: 用ASCII字符画一个盒子.
   3 
   4 # Stefano Palmeri编写,文档作者作了少量编辑.
   5 # 征得作者同意在本书使用.
   6 
   7 
   8 ######################################################################
   9 ###  draw_box 函数的注释  ###
  10 
  11 #  "draw_box" 函数使用户可以在终端上画一个盒子.
  12 #
  13 #
  14 #  用法: draw_box ROW COLUMN HEIGHT WIDTH [COLOR] 
  15 #  ROW 和 COLUMN 定位要画的盒子的左上角.       
  16 #
  17 #  ROW 和 COLUMN 必须要大于0且小于目前终端的尺寸.
  18 #
  19 #  HEIGHT 是盒子的行数,必须 > 0. 
  20 #  HEIGHT + ROW 必须 <= 终端的高度. 
  21 #  WIDTH 是盒子的列数,必须 > 0.
  22 #  WIDTH + COLUMN 必须 <= 终端的宽度.
  23 #
  24 #  例如: 如果你当前终端的尺寸是 20x80,
  25 #  draw_box 2 3 10 45 是合法的
  26 #  draw_box 2 3 19 45 的 HEIGHT 值是错的 (19+2 > 20)
  27 #  draw_box 2 3 18 78 的 WIDTH 值是错的 (78+3 > 80)
  28 #
  29 #  COLOR 是盒子边框的颜色.
  30 #  它是第5个参数,并且它是可选的.
  31 #  0=黑色 1=红色 2=绿色 3=棕褐色 4=蓝色 5=紫色 6=青色 7=白色.
  32 #  如果你传给这个函数错的参数,
  33 #+ 它就会以代码65退出,
  34 #+ 没有其他的信息打印到标准出错上.
  35 #
  36 #  在画盒子之前要清屏.
  37 #  函数内不包含有清屏命令.
  38 #  这使用户可以画多个盒子,甚至叠接多个盒子.
  39 
  40 ###  draw_box 函数注释结束  ### 
  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                 # 如果参数小于4,退出.
  56     exit $E_BADARGS
  57 fi
  58 
  59 # 搜寻参数中的非数字的字符.
  60 # 能用其他更好的办法吗 (留给读者的练习?).
  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 是需要的,因为因为边角的"+"是高和宽共有的部分. 
  66 BOX_WIDTH=`expr $4 - 1`    #
  67 T_ROWS=`tput lines`        #  定义当前终端长和宽的尺寸, 
  68 T_COLS=`tput cols`         #
  69          
  70 if [ $1 -lt 1 ] || [ $1 -gt $T_ROWS ]; then    #  如果参数是数字就开始检查有效性.
  71    exit $E_BADARGS                             #
  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                                 # 参数检查完毕.
  85 
  86 plot_char(){                       # 函数内的函数.
  87    echo -e "\E[${1};${2}H"$3
  88 }
  89 
  90 echo -ne "\E[3${5}m"               # 如果传递了盒子边框颜色参数,则设置它.
  91 
  92 # start drawing the box
  93 
  94 count=1                                         #  用plot_char函数画垂直线
  95 for (( r=$1; count<=$BOX_HEIGHT; r++)); do      #
  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                                        #  用plot_char函数画水平线
 108 for (( c=$2; count<=$BOX_WIDTH; c++)); do      #
 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                   # 画盒子的角.
 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"             #  恢复最初的颜色.
 126 
 127 P_ROWS=`expr $T_ROWS - 1`    #  在终端的底部打印提示符.
 128 
 129 echo -e "\E[${P_ROWS};1H"
 130 }      
 131 
 132 
 133 # 现在, 让我们来画一个盒子.
 134 clear                       # 清屏.
 135 R=2      # 行
 136 C=3      # 列
 137 H=10     # 高
 138 W=45     # 宽
 139 col=1    # 颜色(红)
 140 draw_box $R $C $H $W $col   # 画盒子.
 141 
 142 exit 0
 143 
 144 # 练习:
 145 # --------
 146 # 增加可以在盒子里打印文本的选项

最简单也可能是最有用的ANSI转义序列是加粗文本, \033[1m ... \033[0m. \033 触发转义序列, 而 "[1" 启用加粗属性, 而"[0" 表示切换回禁用加粗状态. "m"则表示终止一个转义序列.
 bash$ echo -e "\033[1mThis is bold text.\033[0m"
 	      

一种相似的转义序列可切换下划线效果 (在 rxvt aterm 上).
 bash$ echo -e "\033[4mThis is underlined text.\033[0m"
 	      

echo使用-e选项可以启用转义序列.

其他的转义序列可用于更改文本或/和背景色彩.

 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
 	      

通常为淡色的前景色文本设置粗体效果是较好的.

tput sgr0 把终端设置恢复为原样. 如果省略这一句会使后续在该终端的输出仍为蓝色.

因为tput sgr0 在某些环境下不能恢复终端设置, echo -ne \E[0m 会是更好的选择.

下表的数值是在 rxvt 终端运行的结果. 具体效果可能在其他的各种终端上不一样.


表 33-1. 转义序列中数值和彩色的对应

色彩 前景色 背景色
3040
3141
绿 3242
3343
3444
洋红 3545
3646
3747


例子 33-13. 显示彩色文本

   1 #!/bin/bash
   2 # color-echo.sh: 用彩色来显示文本.
   3 
   4 # 依照需要修改这个脚本.
   5 # 这比手写彩色的代码更容易一些.
   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"      #  把文本属性重设回原来没有清屏前的
  18                              #
  19 
  20 
  21 cecho ()                     # Color-echo.
  22                              # 参数 $1 = 要显示的信息
  23                              # 参数 $2 = 颜色
  24 {
  25 local default_msg="No message passed."
  26                              # 不是非要一个本地变量.
  27 
  28 message=${1:-$default_msg}   # 默认的信息.
  29 color=${2:-$black}           # 如果没有指定,默认使用黑色.
  30 
  31   echo -e "$color"
  32   echo "$message"
  33   Reset                      # 重设文本属性.
  34 
  35   return
  36 }  
  37 
  38 
  39 # 现在,让我们试试.
  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        # 缺失 $color (色彩)参数.
  48 cecho "\"Empty\" color passed (defaults to black)." ""
  49        # 空的 $color (色彩)参数.
  50 cecho
  51        # $message(信息) 和 $color (色彩)参数都缺失.
  52 cecho "" ""
  53        # 空的 $message (信息)和 $color (色彩)参数.
  54 # ----------------------------------------------------
  55 
  56 echo
  57 
  58 exit 0
  59 
  60 # 练习:
  61 # ---------
  62 # 1) 为'cecho ()'函数增加粗体的效果.
  63 # 2) 增加可选的彩色背景.


例子 33-14. "赛马" 游戏

   1 #!/bin/bash
   2 # horserace.sh: 非常简单的赛马模拟.
   3 # 作者: Stefano Palmeri
   4 # 已取得使用许可.
   5 
   6 ################################################################
   7 #  脚本目的:
   8 #  使用转义字符和终端颜色.
   9 #
  10 #  练习:
  11 #  编辑脚本使其更具有随机性,
  12 #+ 设置一个假的赌场 . . .     
  13 #  嗯 . . . 嗯 . . . 这个开始使我想起了一部电影 . . .
  14 #
  15 #  脚本给每匹马一个随机的障碍.
  16 #  不均等会以障碍来计算
  17 #+ 并且用一种欧洲风格表达出来.
  18 #  例如: 机率(odds)=3.75 意味着如果你押1美元赢,
  19 #+ 你可以赢得3.75美元.
  20 # 
  21 #  脚本已经在GNU/Linux操作系统上测试过 OS,
  22 #+ 测试终端有xterm 和 rxvt, 及 konsole.
  23 #  测试机器有AMD 900 MHz 的处理器,
  24 #+ 平均比赛时间是75秒.    
  25 #  在更快的计算机上比赛时间应该会更低.
  26 #  所以, 如果你想有更多的悬念,重设USLEEP_ARG 变量的值.
  27 #
  28 #  由Stefano Palmeri编写.
  29 ################################################################
  30 
  31 E_RUNERR=65
  32 
  33 # 检查 md5sum 和 bc 是不是安装了. 
  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 #  更改下面的变量值可以使脚本执行的更慢.
  46 #  它会作为usleep的参数 (man usleep)  
  47 #+ 并且它的单位是微秒 (500000微秒 = 半秒).
  48 USLEEP_ARG=0  
  49 
  50 #  如果脚本接收到ctrl-c中断,清除临时目录, 恢复终端光标和颜色 
  51 #
  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 #  参考调试的章节了解'trap'的更多解释
  55 
  56 # 给脚本设置一个唯一(实际不是绝对唯一的)的临时目录名.
  57 HORSE_RACE_TMP_DIR=$HOME/.horserace-`date +%s`-`head -c10 /dev/urandom | md5sum | head -c30`
  58 
  59 # 创建临时目录,并切换到该目录下.
  60 mkdir $HORSE_RACE_TMP_DIR
  61 cd $HORSE_RACE_TMP_DIR
  62 
  63 
  64 #  这个函数把光标移动到行为 $1 列为 $2 然后打印 $3.
  65 #  例如: "move_and_echo 5 10 linux" 等同于
  66 #+ "tput cup 4 9; echo linux", 但是用一个命令代替了两个.
  67 #  注: "tput cup" 表示在终端左上角的 0 0 位置,
  68 #+ echo 是在终端的左上角的 1 1 位置.
  69 move_and_echo() {
  70           echo -ne "\E[${1};${2}H""$3" 
  71 }
  72 
  73 # 产生1-9之间伪随机数的函数. 
  74 random_1_9 () {
  75                 head -c10 /dev/urandom | md5sum | tr -d [a-z] | tr -d 0 | cut -c1 
  76 }
  77 
  78 #  画马时模拟运动的两个函数. 
  79 draw_horse_one() {
  80                echo -n " "//$MOVE_HORSE//
  81 }
  82 draw_horse_two(){
  83               echo -n " "\\\\$MOVE_HORSE\\\\ 
  84 }   
  85 
  86 
  87 # 取得当前的终端尺寸.
  88 N_COLS=`tput cols`
  89 N_LINES=`tput lines`
  90 
  91 # 至少需要 20-行 X 80-列 的终端尺寸. 检查一下.
  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 # 开始画赛场.
 100 
 101 # 需要一个80个字符的字符串,看下面的.
 102 BLANK80=`seq -s "" 100 | head -c80`
 103 
 104 clear
 105 
 106 # 把前景和背景颜色设置成白色的.
 107 echo -ne '\E[37;47m'
 108 
 109 # 把光标移到终端的左上角.
 110 tput cup 0 0 
 111 
 112 # 画六条白线.
 113 for n in `seq 5`; do
 114       echo $BLANK80        # 线是用80个字符组成的字符串.  
 115 done
 116 
 117 # 把前景色设置成黑色. 
 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 # 把前景色设置成红色. 
 132 echo -ne '\E[31m'
 133 
 134 # 一些ASCII艺术.
 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 # 把前景和背景颜色设为绿色.
 148 echo -ne '\E[32;42m'
 149 
 150 # 画11行绿线.
 151 tput cup 5 0
 152 for n in `seq 11`; do
 153       echo $BLANK80
 154 done
 155 
 156 # 把前景色设为黑色. 
 157 echo -ne '\E[30m'
 158 tput cup 5 0
 159 
 160 # 画栅栏. 
 161 echo "++++++++++++++++++++++++++++++++++++++\
 162 ++++++++++++++++++++++++++++++++++++++++++"
 163 
 164 tput cup 15 0
 165 echo "++++++++++++++++++++++++++++++++++++++\
 166 ++++++++++++++++++++++++++++++++++++++++++"
 167 
 168 # 把前景和背景色设回白色.
 169 echo -ne '\E[37;47m'
 170 
 171 # 画3条白线.
 172 for n in `seq 3`; do
 173       echo $BLANK80
 174 done
 175 
 176 # 把前景色设为黑色.
 177 echo -ne '\E[30m'
 178 
 179 # 创建9个文件来保存障碍物.
 180 for n in `seq 10 7 68`; do
 181       touch $n
 182 done  
 183 
 184 # 设置脚本要画的马的类型为第一种类型.
 185 HORSE_TYPE=2
 186 
 187 #  为每匹马创建位置文件和机率文件.
 188 #+ 在这些文件里保存了该匹马当前的位置,
 189 #+ 类型和机率.
 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       # 给马定义随机的障碍物.
 196        HANDICAP=`random_1_9`
 197       # 检查random_1_9函数是否返回了有效值.
 198       while ! echo $HANDICAP | grep [1-9] &> /dev/null; do
 199                 HANDICAP=`random_1_9`
 200       done
 201       # 给马定义最后的障碍的位置. 
 202       LHP=`expr $HANDICAP \* 7 + 3`
 203       for FILE in `seq 10 7 $LHP`; do
 204             echo $HN >> $FILE
 205       done   
 206      
 207       # 计算机率.
 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 # 打印机率.
 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 # 在起跑线上画马.
 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 # 等待回车按键开始赛马.
 251 # 转义序列'\E[?25l'禁显了光标.
 252 tput cup 17 0
 253 echo -e '\E[?25l'Press [enter] key to start the race...
 254 read -s
 255 
 256 #  禁用了终端的常规显示功能.
 257 #  这避免了赛跑时不小心按了按键键入显示字符而弄乱了屏幕.
 258 #  
 259 stty -echo
 260 
 261 # --------------------------------------------------------
 262 # 开始赛跑.
 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 # 设置终点线的列数.
 272 WINNING_POS=74
 273 
 274 # 记录赛跑开始的时间.
 275 START_TIME=`date +%s`
 276 
 277 # COL 是由下面的"while"结构使用的.
 278 COL=0    
 279 
 280 while [ $COL -lt $WINNING_POS ]; do
 281                    
 282           MOVE_HORSE=0     
 283           
 284           # 检查random_1_9函数是否返回了有效值.
 285           while ! echo $MOVE_HORSE | grep [1-9] &> /dev/null; do
 286                 MOVE_HORSE=`random_1_9`
 287           done
 288           
 289           # 取得随机取得的马的类型和当前位置.
 290           HORSE_TYPE=`cat  horse_${MOVE_HORSE}_position | tail -1`
 291           COL=$(expr `cat  horse_${MOVE_HORSE}_position | head -1`) 
 292           
 293           ADD_POS=1
 294           # 检查当前的位置是否是障碍物的位置. 
 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  # 保存新位置.
 307                             
 308          # 选择要画的马的类型.         
 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 # 保存当前类型.
 315          
 316           # 把前景色设为黑,背景色设为绿.
 317           echo -ne '\E[30;42m'
 318           
 319           # 把光标位置移到新的马的位置.
 320           tput cup `expr $MOVE_HORSE + 5`  `cat  horse_${MOVE_HORSE}_position | head -1` 
 321           
 322           # 画马.
 323           $DRAW_HORSE
 324            usleep $USLEEP_ARG
 325           
 326            # 当所有的马都越过15行的之后,再次打印机率.          
 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           # 取得领头的马.
 337           HIGHEST_POS=`cat *position | sort -n | tail -1`          
 338           
 339           # 把背景色重设为白色.
 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 # 取得赛马结束的时间.
 347 FINISH_TIME=`date +%s`
 348 
 349 # 背景色设为绿色并且启用闪动的功能.
 350 echo -ne '\E[30;42m'
 351 echo -en '\E[5m'
 352 
 353 # 使获胜的马闪动.
 354 tput cup `expr $MOVE_HORSE + 5` `cat  horse_${MOVE_HORSE}_position | head -1`
 355 $DRAW_HORSE
 356 
 357 # 禁用闪动文本.
 358 echo -en '\E[25m'
 359 
 360 # 把前景和背景色设为白色.
 361 echo -ne '\E[37;47m'
 362 move_and_echo 18 1 $BLANK80
 363 
 364 # 前景色设为黑色.
 365 echo -ne '\E[30m'
 366 
 367 # 闪动获胜的马.
 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 # 恢复光标和最初的颜色.
 373 echo -en "\E[?25h"
 374 echo -en "\E[0m"
 375 
 376 # 恢复回显功能.
 377 stty echo
 378 
 379 # 删除赛跑的临时文件.
 380 rm -rf $HORSE_RACE_TMP_DIR
 381 
 382 tput cup 19 0
 383 
 384 exit 0

参考 例子 A-22.

然而,有一个主要的问题,那就是ANSI 转义序列是不可移植的. 在一些终端运行的很好的代码可能在另外一些终端上可能运行地很糟糕. 在彩色脚本作者终端上运行的很好的脚本可能在另外一些终端上就产生不可阅读的输出了. 这给彩色脚本的用处大大打了个折扣,而很可能使这些技术变成一个暗机关或只是一个玩具而已.

Moshe Jacobson的颜色工具(http://runslinux.net/projects.html#color)能相当容易地使用ANSI转义序列. 它用清晰和较有逻辑的语法来代替刚才讨论的难用的结构.

Henry/teikedvl 也同样开发了一个软件包来简化彩色脚本的一些操作 (http://scriptechocolor.sourceforge.net/).

[1]

当然,ANSI是American National Standards Institute(美国国家标准组织)的缩写. 这个令人敬畏的组织建立和维护着许多技术和工业的标准.