第三十四章. Bash, 版本 2 和 3

34.1. Bash, 版本2

当前运行在你的机器里的Bash版本号是版本 2.xx.y 或 3.xx.y.
 bash$ echo $BASH_VERSION
 2.05.b.0(1)-release
 	      
经典的Bash版本2编程语言升级版增加了数组变量, [1] 字符串和参数扩展, 和间接变量引用的更好的方法,及其他的属性.


例子 34-1. 字符串扩展

   1 #!/bin/bash
   2 
   3 # 字符串扩展.
   4 # Bash版本2引入的特性.
   5 
   6 #  具有$'xxx'格式的字符串
   7 #+ 将会解释里面的标准的转义字符. 
   8 
   9 echo $'Ringing bell 3 times \a \a \a'
  10      # 可能在一些终端只能响铃一次.
  11 echo $'Three form feeds \f \f \f'
  12 echo $'10 newlines \n\n\n\n\n\n\n\n\n\n'
  13 echo $'\102\141\163\150'   # Bash
  14                            # 八进制相等的字符.
  15 
  16 exit 0


例子 34-2. 间接变量引用 - 新方法

   1 #!/bin/bash
   2 
   3 # 间接变量引用.
   4 # 这有点像C++的引用属性.
   5 
   6 
   7 a=letter_of_alphabet
   8 letter_of_alphabet=z
   9 
  10 echo "a = $a"           # 直接引用.
  11 
  12 echo "Now a = ${!a}"    # 间接引用.
  13 # ${!variable} 形式比老的"eval var1=\$$var2"更高级
  14 
  15 echo
  16 
  17 t=table_cell_3
  18 table_cell_3=24
  19 echo "t = ${!t}"                      # t = 24
  20 table_cell_3=387
  21 echo "Value of t changed to ${!t}"    # 387
  22 
  23 #  这在用来引用数组或表格的成员时非常有用,
  24 #+ 或用来模拟多维数组.
  25 #  如果有可索引的选项 (类似于指针运算)
  26 #+ 会更好. 唉.
  27 
  28 exit 0


例子 34-3. 使用间接变量引用的简单数据库应用

   1 #!/bin/bash
   2 # resistor-inventory.sh
   3 # 使用间接变量引用的简单数据库应用.
   4 
   5 # ============================================================== #
   6 # 数据
   7 
   8 B1723_value=470                                   # 值
   9 B1723_powerdissip=.25                             # 是什么
  10 B1723_colorcode="yellow-violet-brown"             # 色彩带宽
  11 B1723_loc=173                                     # 它们存在哪儿
  12 B1723_inventory=78                                # 有多少
  13 
  14 B1724_value=1000
  15 B1724_powerdissip=.25
  16 B1724_colorcode="brown-black-red"
  17 B1724_loc=24N
  18 B1724_inventory=243
  19 
  20 B1725_value=10000
  21 B1725_powerdissip=.25
  22 B1725_colorcode="brown-black-orange"
  23 B1725_loc=24N
  24 B1725_inventory=89
  25 
  26 # ============================================================== #
  27 
  28 
  29 echo
  30 
  31 PS3='Enter catalog number: '
  32 
  33 echo
  34 
  35 select catalog_number in "B1723" "B1724" "B1725"
  36 do
  37   Inv=${catalog_number}_inventory
  38   Val=${catalog_number}_value
  39   Pdissip=${catalog_number}_powerdissip
  40   Loc=${catalog_number}_loc
  41   Ccode=${catalog_number}_colorcode
  42 
  43   echo
  44   echo "Catalog number $catalog_number:"
  45   echo "There are ${!Inv} of [${!Val} ohm / ${!Pdissip} watt] resistors in stock."
  46   echo "These are located in bin # ${!Loc}."
  47   echo "Their color code is \"${!Ccode}\"."
  48 
  49   break
  50 done
  51 
  52 echo; echo
  53 
  54 # 练习:
  55 # ---------
  56 # 1) 重写脚本,使其从外部文件里读数据.
  57 # 2) 重写脚本,用数组代替间接变量引用
  58 #
  59 #    用数组会更简单明了
  60 
  61 
  62 # 注:
  63 # -----
  64 #  Shell脚本除了最简单的数据应用,其实并不合适数据库应用,
  65 #+ 它过多地依赖实际工作的环境和命令.
  66 #  写数据库应用更好的还是用一门自然支持数据结构的语言,
  67 #+ 如 C++ 或 Java (或甚至是 Perl).
  68 
  69 exit 0


例子 34-4. 用数组和其他的小技巧来处理四人随机打牌

   1 #!/bin/bash
   2 
   3 # Cards:
   4 # 处理四人打牌.
   5 
   6 UNPICKED=0
   7 PICKED=1
   8 
   9 DUPE_CARD=99
  10 
  11 LOWER_LIMIT=0
  12 UPPER_LIMIT=51
  13 CARDS_IN_SUIT=13
  14 CARDS=52
  15 
  16 declare -a Deck
  17 declare -a Suits
  18 declare -a Cards
  19 #  用一个三维数据来描述数据会更容易实现也更明了一些。
  20 #
  21 #  可能Bash将来的版本会支持多维数组.
  22 
  23 
  24 initialize_Deck ()
  25 {
  26 i=$LOWER_LIMIT
  27 until [ "$i" -gt $UPPER_LIMIT ]
  28 do
  29   Deck[i]=$UNPICKED   # 把整副牌的每张牌都设为没人持牌.
  30   let "i += 1"
  31 done
  32 echo
  33 }
  34 
  35 initialize_Suits ()
  36 {
  37 Suits[0]=C #梅花
  38 Suits[1]=D #方块
  39 Suits[2]=H #红心
  40 Suits[3]=S #黑桃
  41 }
  42 
  43 initialize_Cards ()
  44 {
  45 Cards=(2 3 4 5 6 7 8 9 10 J Q K A)
  46 # 另一种初始化数组的方法.
  47 }
  48 
  49 pick_a_card ()
  50 {
  51 card_number=$RANDOM
  52 let "card_number %= $CARDS"
  53 if [ "${Deck[card_number]}" -eq $UNPICKED ]
  54 then
  55   Deck[card_number]=$PICKED
  56   return $card_number
  57 else  
  58   return $DUPE_CARD
  59 fi
  60 }
  61 
  62 parse_card ()
  63 {
  64 number=$1
  65 let "suit_number = number / CARDS_IN_SUIT"
  66 suit=${Suits[suit_number]}
  67 echo -n "$suit-"
  68 let "card_no = number % CARDS_IN_SUIT"
  69 Card=${Cards[card_no]}
  70 printf %-4s $Card
  71 # 优雅地打印各张牌.
  72 }
  73 
  74 seed_random ()  # 随机产生牌上数值的种子.
  75 {               # 如果你没有这么做会有什么发生?
  76 seed=`eval date +%s`
  77 let "seed %= 32766"
  78 RANDOM=$seed
  79 #  其他的产生随机用的种子的方法还有什么W?
  80 #
  81 }
  82 
  83 deal_cards ()
  84 {
  85 echo
  86 
  87 cards_picked=0
  88 while [ "$cards_picked" -le $UPPER_LIMIT ]
  89 do
  90   pick_a_card
  91   t=$?
  92 
  93   if [ "$t" -ne $DUPE_CARD ]
  94   then
  95     parse_card $t
  96 
  97     u=$cards_picked+1
  98     # 改回1步进的索引(临时的). 为什么?
  99     let "u %= $CARDS_IN_SUIT"
 100     if [ "$u" -eq 0 ]   # 内嵌的 if/then 条件测试.
 101     then
 102      echo
 103      echo
 104     fi
 105     # Separate hands.
 106 
 107     let "cards_picked += 1"
 108   fi  
 109 done  
 110 
 111 echo
 112 
 113 return 0
 114 }
 115 
 116 
 117 # 结构化编程:
 118 # 整个程序逻辑模块化.
 119 
 120 #================
 121 seed_random
 122 initialize_Deck
 123 initialize_Suits
 124 initialize_Cards
 125 deal_cards
 126 #================
 127 
 128 exit 0
 129 
 130 
 131 
 132 # 练习 1:
 133 # 把这个脚本完整地做注释.
 134 
 135 # 练习 2:
 136 # 增加一个处理例程 (函数) 来以花色排序打印出每个人手中的牌.
 137 # 如果你高兴,可增加你喜欢的各种酷的代码.
 138 
 139 # 练习 3:
 140 # 简化和理顺脚本的逻辑.

[1]

Chet Ramey 承诺会在Bash的未来版本中实现关联数组(associative arrays) (一个Perl特性). 到了版本3,这个特性还没有实现.