ANSI [1] 转义序列设置屏幕属性,例如粗体文本以及前景色和背景色。 DOS 批处理文件 常使用 ANSI 转义码进行颜色输出,Bash 脚本也可以这样做。
示例 36-13. “着色” 地址数据库
#!/bin/bash # ex30a.sh: "Colorized" version of ex30.sh. # Crude address database clear # Clear the screen. echo -n " " echo -e '\E[37;44m'"\033[1mContact List\033[0m" # White on blue background echo; echo echo -e "\033[1mChoose one of the following persons:\033[0m" # Bold tput sgr0 # Reset attributes. echo "(Enter only the first letter of name.)" echo echo -en '\E[47;34m'"\033[1mE\033[0m" # Blue tput sgr0 # Reset colors to "normal." echo "vans, Roland" # "[E]vans, Roland" echo -en '\E[47;35m'"\033[1mJ\033[0m" # Magenta tput sgr0 echo "ambalaya, Mildred" echo -en '\E[47;32m'"\033[1mS\033[0m" # Green tput sgr0 echo "mith, Julie" echo -en '\E[47;31m'"\033[1mZ\033[0m" # Red tput sgr0 echo "ane, Morris" echo read person case "$person" in # Note variable is quoted. "E" | "e" ) # Accept upper or lowercase input. echo echo "Roland Evans" echo "4321 Flash Dr." echo "Hardscrabble, CO 80753" echo "(303) 734-9874" echo "(303) 734-9892 fax" echo "revans@zzy.net" echo "Business partner & old friend" ;; "J" | "j" ) echo echo "Mildred Jambalaya" echo "249 E. 7th St., Apt. 19" echo "New York, NY 10009" echo "(212) 533-2814" echo "(212) 533-9972 fax" echo "milliej@loisaida.com" echo "Girlfriend" echo "Birthday: Feb. 11" ;; # Add info for Smith & Zane later. * ) # Default option. # Empty input (hitting RETURN) fits here, too. echo echo "Not yet in database." ;; esac tput sgr0 # Reset colors to "normal." echo exit 0 |
示例 36-14. 绘制方框
#!/bin/bash # Draw-box.sh: Drawing a box using ASCII characters. # Script by Stefano Palmeri, with minor editing by document author. # Minor edits suggested by Jim Angstadt. # Used in the ABS Guide with permission. ###################################################################### ### draw_box function doc ### # The "draw_box" function lets the user #+ draw a box in a terminal. # # Usage: draw_box ROW COLUMN HEIGHT WIDTH [COLOR] # ROW and COLUMN represent the position #+ of the upper left angle of the box you're going to draw. # ROW and COLUMN must be greater than 0 #+ and less than current terminal dimension. # HEIGHT is the number of rows of the box, and must be > 0. # HEIGHT + ROW must be <= than current terminal height. # WIDTH is the number of columns of the box and must be > 0. # WIDTH + COLUMN must be <= than current terminal width. # # E.g.: If your terminal dimension is 20x80, # draw_box 2 3 10 45 is good # draw_box 2 3 19 45 has bad HEIGHT value (19+2 > 20) # draw_box 2 3 18 78 has bad WIDTH value (78+3 > 80) # # COLOR is the color of the box frame. # This is the 5th argument and is optional. # 0=black 1=red 2=green 3=tan 4=blue 5=purple 6=cyan 7=white. # If you pass the function bad arguments, #+ it will just exit with code 65, #+ and no messages will be printed on stderr. # # Clear the terminal before you start to draw a box. # The clear command is not contained within the function. # This allows the user to draw multiple boxes, even overlapping ones. ### end of draw_box function doc ### ###################################################################### draw_box(){ #=============# HORZ="-" VERT="|" CORNER_CHAR="+" MINARGS=4 E_BADARGS=65 #=============# if [ $# -lt "$MINARGS" ]; then # If args are less than 4, exit. exit $E_BADARGS fi # Looking for non digit chars in arguments. # Probably it could be done better (exercise for the reader?). if echo $@ | tr -d [:blank:] | tr -d [:digit:] | grep . &> /dev/null; then exit $E_BADARGS fi BOX_HEIGHT=`expr $3 - 1` # -1 correction needed because angle char "+" BOX_WIDTH=`expr $4 - 1` #+ is a part of both box height and width. T_ROWS=`tput lines` # Define current terminal dimension T_COLS=`tput cols` #+ in rows and columns. if [ $1 -lt 1 ] || [ $1 -gt $T_ROWS ]; then # Start checking if arguments exit $E_BADARGS #+ are correct. fi if [ $2 -lt 1 ] || [ $2 -gt $T_COLS ]; then exit $E_BADARGS fi if [ `expr $1 + $BOX_HEIGHT + 1` -gt $T_ROWS ]; then exit $E_BADARGS fi if [ `expr $2 + $BOX_WIDTH + 1` -gt $T_COLS ]; then exit $E_BADARGS fi if [ $3 -lt 1 ] || [ $4 -lt 1 ]; then exit $E_BADARGS fi # End checking arguments. plot_char(){ # Function within a function. echo -e "\E[${1};${2}H"$3 } echo -ne "\E[3${5}m" # Set box frame color, if defined. # start drawing the box count=1 # Draw vertical lines using for (( r=$1; count<=$BOX_HEIGHT; r++)); do #+ plot_char function. plot_char $r $2 $VERT let count=count+1 done count=1 c=`expr $2 + $BOX_WIDTH` for (( r=$1; count<=$BOX_HEIGHT; r++)); do plot_char $r $c $VERT let count=count+1 done count=1 # Draw horizontal lines using for (( c=$2; count<=$BOX_WIDTH; c++)); do #+ plot_char function. plot_char $1 $c $HORZ let count=count+1 done count=1 r=`expr $1 + $BOX_HEIGHT` for (( c=$2; count<=$BOX_WIDTH; c++)); do plot_char $r $c $HORZ let count=count+1 done plot_char $1 $2 $CORNER_CHAR # Draw box angles. plot_char $1 `expr $2 + $BOX_WIDTH` $CORNER_CHAR plot_char `expr $1 + $BOX_HEIGHT` $2 $CORNER_CHAR plot_char `expr $1 + $BOX_HEIGHT` `expr $2 + $BOX_WIDTH` $CORNER_CHAR echo -ne "\E[0m" # Restore old colors. P_ROWS=`expr $T_ROWS - 1` # Put the prompt at bottom of the terminal. echo -e "\E[${P_ROWS};1H" } # Now, let's try drawing a box. clear # Clear the terminal. R=2 # Row C=3 # Column H=10 # Height W=45 # Width col=1 # Color (red) draw_box $R $C $H $W $col # Draw the box. exit 0 # Exercise: # -------- # Add the option of printing text within the drawn box. |
最简单,也许也是最有用的 ANSI 转义序列是粗体文本,\033[1m ... \033[0m。\033 代表 escape(转义),“[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 可能是更好的选择。 |
使用以下模板编写彩色背景上的彩色文本。 echo -e '\E[COLOR1;COLOR2m在这里输入一些文本' “\E[” 开始转义序列。分号分隔的数字 “COLOR1” 和 “COLOR2” 根据下表指定前景色和背景色。(数字的顺序无关紧要,因为前景色和背景色数字落在不重叠的范围内。)“m” 终止转义序列,文本紧随其后开始。 另请注意,单引号 括起 echo -e 后的命令序列的其余部分。 |
下表中的数字适用于 rxvt 终端。对于其他终端仿真器,结果可能会有所不同。
示例 36-15. 回显彩色文本
#!/bin/bash # color-echo.sh: Echoing text messages in color. # Modify this script for your own purposes. # It's easier than hand-coding color. black='\E[30;47m' red='\E[31;47m' green='\E[32;47m' yellow='\E[33;47m' blue='\E[34;47m' magenta='\E[35;47m' cyan='\E[36;47m' white='\E[37;47m' alias Reset="tput sgr0" # Reset text attributes to normal #+ without clearing screen. cecho () # Color-echo. # Argument $1 = message # Argument $2 = color { local default_msg="No message passed." # Doesn't really need to be a local variable. message=${1:-$default_msg} # Defaults to default message. color=${2:-$black} # Defaults to black, if not specified. echo -e "$color" echo "$message" Reset # Reset to normal. return } # Now, let's try it out. # ---------------------------------------------------- cecho "Feeling blue..." $blue cecho "Magenta looks more like purple." $magenta cecho "Green with envy." $green cecho "Seeing red?" $red cecho "Cyan, more familiarly known as aqua." $cyan cecho "No color passed (defaults to black)." # Missing $color argument. cecho "\"Empty\" color passed (defaults to black)." "" # Empty $color argument. cecho # Missing $message and $color arguments. cecho "" "" # Empty $message and $color arguments. # ---------------------------------------------------- echo exit 0 # Exercises: # --------- # 1) Add the "bold" attribute to the 'cecho ()' function. # 2) Add options for colored backgrounds. |
示例 36-16. “赛马” 游戏
#!/bin/bash # horserace.sh: Very simple horserace simulation. # Author: Stefano Palmeri # Used with permission. ################################################################ # Goals of the script: # playing with escape sequences and terminal colors. # # Exercise: # Edit the script to make it run less randomly, #+ set up a fake betting shop . . . # Um . . . um . . . it's starting to remind me of a movie . . . # # The script gives each horse a random handicap. # The odds are calculated upon horse handicap #+ and are expressed in European(?) style. # E.g., odds=3.75 means that if you bet $1 and win, #+ you receive $3.75. # # The script has been tested with a GNU/Linux OS, #+ using xterm and rxvt, and konsole. # On a machine with an AMD 900 MHz processor, #+ the average race time is 75 seconds. # On faster computers the race time would be lower. # So, if you want more suspense, reset the USLEEP_ARG variable. # # Script by Stefano Palmeri. ################################################################ E_RUNERR=65 # Check if md5sum and bc are installed. if ! which bc &> /dev/null; then echo bc is not installed. echo "Can\'t run . . . " exit $E_RUNERR fi if ! which md5sum &> /dev/null; then echo md5sum is not installed. echo "Can\'t run . . . " exit $E_RUNERR fi # Set the following variable to slow down script execution. # It will be passed as the argument for usleep (man usleep) #+ and is expressed in microseconds (500000 = half a second). USLEEP_ARG=0 # Clean up the temp directory, restore terminal cursor and #+ terminal colors -- if script interrupted by Ctl-C. trap 'echo -en "\E[?25h"; echo -en "\E[0m"; stty echo;\ tput cup 20 0; rm -fr $HORSE_RACE_TMP_DIR' TERM EXIT # See the chapter on debugging for an explanation of 'trap.' # Set a unique (paranoid) name for the temp directory the script needs. HORSE_RACE_TMP_DIR=$HOME/.horserace-`date +%s`-`head -c10 /dev/urandom \ | md5sum | head -c30` # Create the temp directory and move right in. mkdir $HORSE_RACE_TMP_DIR cd $HORSE_RACE_TMP_DIR # This function moves the cursor to line $1 column $2 and then prints $3. # E.g.: "move_and_echo 5 10 linux" is equivalent to #+ "tput cup 4 9; echo linux", but with one command instead of two. # Note: "tput cup" defines 0 0 the upper left angle of the terminal, #+ echo defines 1 1 the upper left angle of the terminal. move_and_echo() { echo -ne "\E[${1};${2}H""$3" } # Function to generate a pseudo-random number between 1 and 9. random_1_9 () { head -c10 /dev/urandom | md5sum | tr -d [a-z] | tr -d 0 | cut -c1 } # Two functions that simulate "movement," when drawing the horses. draw_horse_one() { echo -n " "//$MOVE_HORSE// } draw_horse_two(){ echo -n " "\\\\$MOVE_HORSE\\\\ } # Define current terminal dimension. N_COLS=`tput cols` N_LINES=`tput lines` # Need at least a 20-LINES X 80-COLUMNS terminal. Check it. if [ $N_COLS -lt 80 ] || [ $N_LINES -lt 20 ]; then echo "`basename $0` needs a 80-cols X 20-lines terminal." echo "Your terminal is ${N_COLS}-cols X ${N_LINES}-lines." exit $E_RUNERR fi # Start drawing the race field. # Need a string of 80 chars. See below. BLANK80=`seq -s "" 100 | head -c80` clear # Set foreground and background colors to white. echo -ne '\E[37;47m' # Move the cursor on the upper left angle of the terminal. tput cup 0 0 # Draw six white lines. for n in `seq 5`; do echo $BLANK80 # Use the 80 chars string to colorize the terminal. done # Sets foreground color to black. echo -ne '\E[30m' move_and_echo 3 1 "START 1" move_and_echo 3 75 FINISH move_and_echo 1 5 "|" move_and_echo 1 80 "|" move_and_echo 2 5 "|" move_and_echo 2 80 "|" move_and_echo 4 5 "| 2" move_and_echo 4 80 "|" move_and_echo 5 5 "V 3" move_and_echo 5 80 "V" # Set foreground color to red. echo -ne '\E[31m' # Some ASCII art. move_and_echo 1 8 "..@@@..@@@@@...@@@@@.@...@..@@@@..." move_and_echo 2 8 ".@...@...@.......@...@...@.@......." move_and_echo 3 8 ".@@@@@...@.......@...@@@@@.@@@@...." move_and_echo 4 8 ".@...@...@.......@...@...@.@......." move_and_echo 5 8 ".@...@...@.......@...@...@..@@@@..." move_and_echo 1 43 "@@@@...@@@...@@@@..@@@@..@@@@." move_and_echo 2 43 "@...@.@...@.@.....@.....@....." move_and_echo 3 43 "@@@@..@@@@@.@.....@@@@...@@@.." move_and_echo 4 43 "@..@..@...@.@.....@.........@." move_and_echo 5 43 "@...@.@...@..@@@@..@@@@.@@@@.." # Set foreground and background colors to green. echo -ne '\E[32;42m' # Draw eleven green lines. tput cup 5 0 for n in `seq 11`; do echo $BLANK80 done # Set foreground color to black. echo -ne '\E[30m' tput cup 5 0 # Draw the fences. echo "++++++++++++++++++++++++++++++++++++++\ ++++++++++++++++++++++++++++++++++++++++++" tput cup 15 0 echo "++++++++++++++++++++++++++++++++++++++\ ++++++++++++++++++++++++++++++++++++++++++" # Set foreground and background colors to white. echo -ne '\E[37;47m' # Draw three white lines. for n in `seq 3`; do echo $BLANK80 done # Set foreground color to black. echo -ne '\E[30m' # Create 9 files to stores handicaps. for n in `seq 10 7 68`; do touch $n done # Set the first type of "horse" the script will draw. HORSE_TYPE=2 # Create position-file and odds-file for every "horse". #+ In these files, store the current position of the horse, #+ the type and the odds. for HN in `seq 9`; do touch horse_${HN}_position touch odds_${HN} echo \-1 > horse_${HN}_position echo $HORSE_TYPE >> horse_${HN}_position # Define a random handicap for horse. HANDICAP=`random_1_9` # Check if the random_1_9 function returned a good value. while ! echo $HANDICAP | grep [1-9] &> /dev/null; do HANDICAP=`random_1_9` done # Define last handicap position for horse. LHP=`expr $HANDICAP \* 7 + 3` for FILE in `seq 10 7 $LHP`; do echo $HN >> $FILE done # Calculate odds. case $HANDICAP in 1) ODDS=`echo $HANDICAP \* 0.25 + 1.25 | bc` echo $ODDS > odds_${HN} ;; 2 | 3) ODDS=`echo $HANDICAP \* 0.40 + 1.25 | bc` echo $ODDS > odds_${HN} ;; 4 | 5 | 6) ODDS=`echo $HANDICAP \* 0.55 + 1.25 | bc` echo $ODDS > odds_${HN} ;; 7 | 8) ODDS=`echo $HANDICAP \* 0.75 + 1.25 | bc` echo $ODDS > odds_${HN} ;; 9) ODDS=`echo $HANDICAP \* 0.90 + 1.25 | bc` echo $ODDS > odds_${HN} esac done # Print odds. print_odds() { tput cup 6 0 echo -ne '\E[30;42m' for HN in `seq 9`; do echo "#$HN odds->" `cat odds_${HN}` done } # Draw the horses at starting line. draw_horses() { tput cup 6 0 echo -ne '\E[30;42m' for HN in `seq 9`; do echo /\\$HN/\\" " done } print_odds echo -ne '\E[47m' # Wait for a enter key press to start the race. # The escape sequence '\E[?25l' disables the cursor. tput cup 17 0 echo -e '\E[?25l'Press [enter] key to start the race... read -s # Disable normal echoing in the terminal. # This avoids key presses that might "contaminate" the screen #+ during the race. stty -echo # -------------------------------------------------------- # Start the race. draw_horses echo -ne '\E[37;47m' move_and_echo 18 1 $BLANK80 echo -ne '\E[30m' move_and_echo 18 1 Starting... sleep 1 # Set the column of the finish line. WINNING_POS=74 # Define the time the race started. START_TIME=`date +%s` # COL variable needed by following "while" construct. COL=0 while [ $COL -lt $WINNING_POS ]; do MOVE_HORSE=0 # Check if the random_1_9 function has returned a good value. while ! echo $MOVE_HORSE | grep [1-9] &> /dev/null; do MOVE_HORSE=`random_1_9` done # Define old type and position of the "randomized horse". HORSE_TYPE=`cat horse_${MOVE_HORSE}_position | tail -n 1` COL=$(expr `cat horse_${MOVE_HORSE}_position | head -n 1`) ADD_POS=1 # Check if the current position is an handicap position. if seq 10 7 68 | grep -w $COL &> /dev/null; then if grep -w $MOVE_HORSE $COL &> /dev/null; then ADD_POS=0 grep -v -w $MOVE_HORSE $COL > ${COL}_new rm -f $COL mv -f ${COL}_new $COL else ADD_POS=1 fi else ADD_POS=1 fi COL=`expr $COL + $ADD_POS` echo $COL > horse_${MOVE_HORSE}_position # Store new position. # Choose the type of horse to draw. case $HORSE_TYPE in 1) HORSE_TYPE=2; DRAW_HORSE=draw_horse_two ;; 2) HORSE_TYPE=1; DRAW_HORSE=draw_horse_one esac echo $HORSE_TYPE >> horse_${MOVE_HORSE}_position # Store current type. # Set foreground color to black and background to green. echo -ne '\E[30;42m' # Move the cursor to new horse position. tput cup `expr $MOVE_HORSE + 5` \ `cat horse_${MOVE_HORSE}_position | head -n 1` # Draw the horse. $DRAW_HORSE usleep $USLEEP_ARG # When all horses have gone beyond field line 15, reprint odds. touch fieldline15 if [ $COL = 15 ]; then echo $MOVE_HORSE >> fieldline15 fi if [ `wc -l fieldline15 | cut -f1 -d " "` = 9 ]; then print_odds : > fieldline15 fi # Define the leading horse. HIGHEST_POS=`cat *position | sort -n | tail -1` # Set background color to white. echo -ne '\E[47m' tput cup 17 0 echo -n Current leader: `grep -w $HIGHEST_POS *position | cut -c7`\ " " done # Define the time the race finished. FINISH_TIME=`date +%s` # Set background color to green and enable blinking text. echo -ne '\E[30;42m' echo -en '\E[5m' # Make the winning horse blink. tput cup `expr $MOVE_HORSE + 5` \ `cat horse_${MOVE_HORSE}_position | head -n 1` $DRAW_HORSE # Disable blinking text. echo -en '\E[25m' # Set foreground and background color to white. echo -ne '\E[37;47m' move_and_echo 18 1 $BLANK80 # Set foreground color to black. echo -ne '\E[30m' # Make winner blink. tput cup 17 0 echo -e "\E[5mWINNER: $MOVE_HORSE\E[25m"" Odds: `cat odds_${MOVE_HORSE}`"\ " Race time: `expr $FINISH_TIME - $START_TIME` secs" # Restore cursor and old colors. echo -en "\E[?25h" echo -en "\E[0m" # Restore echoing. stty echo # Remove race temp directory. rm -rf $HORSE_RACE_TMP_DIR tput cup 19 0 exit 0 |
另请参见 示例 A-21、示例 A-44、示例 A-52 和 示例 A-40。
![]() | 然而,所有这一切都存在一个主要问题。ANSI 转义序列绝对是 不可移植的。 在某些终端仿真器(或控制台)上运行良好的代码,在其他终端上可能运行不同,甚至根本无法运行。在脚本作者的机器上看起来令人惊艳的 “着色” 脚本,在别人的机器上可能会产生无法读取的输出。这在一定程度上损害了着色脚本的实用性,并可能将这项技术降级为一种噱头。着色脚本可能不适合商业环境,也就是说,您的主管可能不赞成。 |
Alister 的 ansi-color 实用程序(基于 Moshe Jacobson 的 color 实用程序)大大简化了 ANSI 转义序列的使用。它用简洁而逻辑的语法代替了刚刚讨论的笨拙结构。
Henry/teikedvl 也创建了一个实用程序 (http://scriptechocolor.sourceforge.net/) 来简化着色脚本的创建。
[1] | ANSI 当然是美国国家标准协会的缩写。这个令人尊敬的机构建立并维护各种技术和工业标准。 |