分类:
2010-01-03 22:37:09
摘自《高级bash脚本编程指南》
对代码块的操作是构造和组织shell脚本的关键. 循环和分支结构为脚本编程提供了操作代码块的工具.
循环就是迭代(重复)一些命令的代码块, 如果循环控制条件不满足的话, 就结束循环.
for循环
for arg in [list]
这是一个基本的循环结构. 它与C语言中的for循环结构有很大的不同.
for arg in [list]
do
command(s)...
done
在循环的每次执行中, arg将顺序的访问list中列出的变量.
1 for arg in "$var1" "$var2" "$var3" ... "$varN" 2 # 在第1次循环中, arg = $var1 3 # 在第2次循环中, arg = $var2 4 # 在第3次循环中, arg = $var3 5 # ... 6 # 在第N此循环中, arg = $varN 7 8 # 在[list]中的参数加上双引号是为了阻止单词分割.
list中的参数允许包含通配符.
如果do和for想在同一行中出现, 那么在它们之间需要添加一个分号.
for arg in [list] ; do
例子 1. 一个简单的for循环
1 #!/bin/bash 2 # 列出所有的行星名称. (译者注: 现在的太阳系行星已经有了变化^_^) 3 4 for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto 5 do 6 echo $planet # 每个行星都被单独打印在一行上. 7 done 8 9 echo 10 11 for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto" 12 # 所有的行星名称都打印在同一行上. 13 # 整个'list'都被双引号封成了一个变量. 14 do 15 echo $planet 16 done 17 18 exit 0
每个[list]中的元素都可能包含多个参数. 在处理参数组时, 这是非常有用的. 在这种情况下, 使用set命令来强制解析(单词分割)每个[list]中的元素, 并且将每个解析出来的部分都分配到一个位置参数中.
例子 2. 每个[list]元素中都带有两个参数的for循环
1 #!/bin/bash
2 # 还是行星.
3
4 # 用行星距太阳的距离来分配行星的名字.
5
6 for planet in "Mercury 36" "Venus 67" "Earth 93" "Mars 142" "Jupiter 483"
7 do
8 set -- $planet # 解析变量"planet"并且设置位置参数.
9 # "--" 将防止$planet为空, 或者是以一个破折号开头.
10
11 # 可能需要保存原始的位置参数, 因为它们被覆盖了.
12 # 一种方法就是使用数组.
13 # original_params=("$@")
14
15 echo "$1 $2,000,000 miles from the sun"
16 #-------two tabs---把后边的0和2连接起来
17 done
18
19 # (感谢, S.C., 对此问题进行的澄清.)
20
21 exit 0
可以将一个变量放在for循环的[list]位置上.
例子 3. 文件信息: 对包含在变量中的文件列表进行操作
1 #!/bin/bash
2 # fileinfo.sh
3
4 FILES="/usr/sbin/accept
5 /usr/sbin/pwck
6 /usr/sbin/chroot
7 /usr/bin/fakefile
8 /sbin/badblocks
9 /sbin/ypbind" # 这是你所关心的文件列表.
10 # 扔进去一个假文件, /usr/bin/fakefile.
11
12 echo
13
14 for file in $FILES
15 do
16
17 if [ ! -e "$file" ] # 检查文件是否存在.
18 then
19 echo "$file does not exist."; echo
20 continue # 继续下一个.
21 fi
22
23 ls -l $file | awk '{ print $9 " file size: " $5 }' # 打印两个域.
24 whatis `basename $file` # 文件信息.
25 # 注意whatis数据库需要提前建立好.
26 # 要想达到这个目的, 以root身份运行/usr/bin/makewhatis.
27 echo
28 done
29
30 exit 0
如果在for循环的[list]中有通配符 (*和?), 那么将会发生通配(globbing), 也就是文件名扩展.
例子 4. 在for循环中操作文件
1 #!/bin/bash 2 # list-glob.sh: 使用"globbing", 在for循环中产生[list] 3 4 echo 5 6 for file in * 7 # ^ 在表达式中识别文件名匹配时, 8 #+ Bash将执行文件名扩展. 9 do 10 ls -l "$file" # 列出在$PWD(当前目录)中的所有文件. 11 # 回想一下,通配符"*"能够匹配所有文件, 12 #+ 然而,在"globbing"中,是不能比配"."文件的. 13 14 # 如果没匹配到任何文件,那它将扩展成自己. 15 # 为了不让这种情况发生,那就设置nullglob选项 16 #+ (shopt -s nullglob). 17 # 感谢, S.C. 18 done 19 20 echo; echo 21 22 for file in [jx]* 23 do 24 rm -f $file # 只删除当前目录下以"j"或"x"开头的文件. 25 echo "Removed file \"$file\"". 26 done 27 28 echo 29 30 exit 0
在一个for循环中忽略in [list]部分的话, 将会使循环操作$@ — 从命令行传递给脚本的位置参数.
例子 5. 在for循环中省略 in [list] 部分
1 #!/bin/bash 2 3 # 使用两种方式来调用这个脚本, 一种带参数, 另一种不带参数, 4 #+ 并观察在这两种情况下, 此脚本的行为. 5 6 for a 7 do 8 echo -n "$a " 9 done 10 11 # 省略'in list'部分, 因此循环将会操作'$@' 12 #+ (包括空白的命令行参数列表). 13 14 echo 15 16 exit 0
也可以使用命令替换 来产生for循环的[list].
例子 6. 使用命令替换来产生for循环的[list]
1 #!/bin/bash 2 # for-loopcmd.sh: 带[list]的for循环, 3 #+ [list]是由命令替换所产生的. 4 5 NUMBERS="9 7 3 8 37.53" 6 7 for number in `echo $NUMBERS` # for number in 9 7 3 8 37.53 8 do 9 echo -n "$number " 10 done 11 12 echo 13 exit 0
例子 7. 列出系统上的所有用户
1 #!/bin/bash
2 # userlist.sh
3
4 PASSWORD_FILE=/etc/passwd
5 n=1 # User number
6
7 for name in $(awk 'BEGIN{FS=":"}{print $1}' < "$PASSWORD_FILE" )
8 # 域分隔 = : ^^^^^^
9 # 打印出第一个域 ^^^^^^^^
10 # 从password文件中取得输入 ^^^^^^^^^^^^^^^^^
11 do
12 echo "USER #$n = $name"
13 let "n += 1"
14 done
15
16
17 # USER #1 = root
18 # USER #2 = bin
19 # USER #3 = daemon
20 # ...
21 # USER #30 = bozo
22
23 exit 0
24
25 # 练习:
26 # -----
27 # 一个普通用户(或者是一个普通用户运行的脚本)
28 #+ 怎么才能够读取/etc/passwd呢?
29 # 这是否是一个安全漏洞? 为什么是?为什么不是?
for循环的输出也可以通过管道传递到一个或多个命令中.
例子 8. 列出目录中所有的符号链接
1 #!/bin/bash
2 # symlinks.sh: 列出目录中所有的符号链接文件.
3
4
5 directory=${1-`pwd`}
6 # 如果没有其他特殊的指定,
7 #+ 默认为当前工作目录.
8 # 下边的代码块, 和上边这句等价.
9 # ----------------------------------------------------------
10 # ARGS=1 # 需要一个命令行参数.
11 #
12 # if [ $# -ne "$ARGS" ] # 如果不是单个参数的话...
13 # then
14 # directory=`pwd` # 当前工作目录
15 # else
16 # directory=$1
17 # fi
18 # ----------------------------------------------------------
19
20 echo "symbolic links in directory \"$directory\""
21
22 for file in "$( find $directory -type l )" # -type l = 符号链接
23 do
24 echo "$file"
25 done | sort # 否则的话, 列出的文件都是未经排序的.
26 # 严格意义上说, 这里并不一定非要一个循环不可.
27 #+ 因为"find"命令的输出将被扩展成一个单词.
28 # 然而, 这种方式很容易理解也很容易说明.
29
30 # 就像Dominik 'Aeneas' Schnitzer所指出的,
31 #+ 如果没将$( find $directory -type l )用""引用起来的话,
32 #+ 那么将会把一个带有空白部分的文件名拆分成以空白分隔的两部分(文件名允许有空白).
33 # 即使这里只会取出每个参数的第一个域.
34
35 exit 0
36
37
38 # Jean Helou建议采用下边的方法:
39
40 echo "symbolic links in directory \"$directory\""
41 # 当前IFS的备份. 要小心使用这个值.
42 OLDIFS=$IFS
43 IFS=:
44
45 for file in $(find $directory -type l -printf "%p$IFS")
46 do # ^^^^^^^^^^^^^^^^
47 echo "$file"
48 done|sort
循环的stdout可以重定向到文件中, 我们对上边的例子做了一点修改.
例子 9. 将目录中所有符号链接文件的名字保存到一个文件中
1 #!/bin/bash
2 # symlinks.sh: 列出目录中所有的符号链接文件.
3
4 OUTFILE=symlinks.list # 保存符号链接文件名的文件
5
6 directory=${1-`pwd`}
7 # 如果没有其他特殊的指定,
8 #+ 默认为当前工作目录.
9
10
11 echo "symbolic links in directory \"$directory\"" > "$OUTFILE"
12 echo "---------------------------" >> "$OUTFILE"
13
14 for file in "$( find $directory -type l )" # -type l = 符号链接
15 do
16 echo "$file"
17 done | sort >> "$OUTFILE" # 循环的stdout
18 # ^^^^^^^^^^^^^ 重定向到一个文件中.
19
20 exit 0
有一种非常像C语言for循环的语法形式. 需要使用(()).
例子 10. 一个C风格的for循环
1 #!/bin/bash 2 # 两种循环到10的方法. 3 4 echo 5 6 # 标准语法. 7 for a in 1 2 3 4 5 6 7 8 9 10 8 do 9 echo -n "$a " 10 done 11 12 echo; echo 13 14 # +==========================================+ 15 16 # 现在, 让我们用C风格语法来做相同的事情. 17 18 LIMIT=10 19 20 for ((a=1; a <= LIMIT ; a++)) # 双圆括号, 并且"LIMIT"变量前面没有"$". 21 do 22 echo -n "$a " 23 done # 这是一个借用'ksh93'的结构. 24 25 echo; echo 26 27 # +=========================================================================+ 28 29 # 让我们使用C语言的"逗号操作符", 来同时增加两个变量的值. 30 31 for ((a=1, b=1; a <= LIMIT ; a++, b++)) # 逗号将同时进行两条操作. 32 do 33 echo -n "$a-$b " 34 done 35 36 echo; echo 37 38 exit 0
while
这种结构在循环的开头判断条件是否满足, 如果条件一直满足, 那么就一直循环下去 (返回0作为退出状态码). 与for循环的区别是, while循环更适合在循环次数未知的情况下使用.
while [condition]
do
command...
done
与for循环一样, 如果想把do和条件判断放到同一行上的话, 还是需要一个分号.
while [condition] ; do
例子 11. while循环
1 #!/bin/bash 2 3 echo 4 # 等价于: 5 while [ "$var1" != "end" ] # while test "$var1" != "end" 6 do 7 echo "Input variable #1 (end to exit) " 8 read var1 # 为什么不使用'read $var1'? 9 echo "variable #1 = $var1" # 因为包含"#", 所以需要"" 10 # 如果输入为'end', 那么就在这里echo. 11 # 不在这里判断结束, 在循环顶判断. 12 echo 13 done 14 15 exit 0
一个while循环可以有多个判断条件. 但是只有最后一个才能够决定是否能够退出循环. 然而这里需要一种有点特殊的循环语法.
例子 12. 多条件的while循环
1 #!/bin/bash 2 3 var1=unset 4 previous=$var1 5 6 while echo "previous-variable = $previous" 7 echo 8 previous=$var1 9 [ "$var1" != end ] # 纪录之前的$var1. 10 # 这个"while"中有4个条件, 但是只有最后一个能够控制循环. 11 # *最后*的退出状态就是由这最后一个条件来决定. 12 do 13 echo "Input variable #1 (end to exit) " 14 read var1 15 echo "variable #1 = $var1" 16 done 17 18 # 尝试理解这个脚本的运行过程. 19 # 这里还是有点小技巧的. 20 21 exit 0
与for循环一样, while循环也可以通过(())来使用C风格的语法.
例子 13. C风格的while循环
1 #!/bin/bash 2 # wh-loopc.sh: 循环10次的"while"循环. 3 4 LIMIT=10 5 a=1 6 7 while [ "$a" -le $LIMIT ] 8 do 9 echo -n "$a " 10 let "a+=1" 11 done # 到目前为止都没有什么令人惊奇的地方. 12 13 echo; echo 14 15 # +=================================================================+ 16 17 # 现在, 重复C风格的语法. 18 19 ((a = 1)) # a=1 20 # 双圆括号允许赋值两边的空格, 就像C语言一样. 21 22 while (( a <= LIMIT )) # 双圆括号, 变量前边没有"$". 23 do 24 echo -n "$a " 25 ((a += 1)) # let "a+=1" 26 # Yes, 看到了吧. 27 # 双圆括号允许像C风格的语法一样增加变量的值. 28 done 29 30 echo 31 32 # 现在, C程序员可以在Bash中找到回家的感觉了吧. 33 34 exit 0
while循环的stdin可以使用<来重定向到一个文件.while循环的stdin支持管道.
until
这个结构在循环的顶部判断条件, 并且如果条件一直为false, 那么就一直循环下去. (与while循环相反).
until [condition-is-true]
do
command...
done
注意, until循环的条件判断在循环的顶部, 这与某些编程语言是不同的.
与for循环一样, 如果想把do和条件判断放在同一行里, 那么就需要使用分号.
until [condition-is-true] ; do
例子 14. until循环
1 #!/bin/bash 2 3 END_CONDITION=end 4 5 until [ "$var1" = "$END_CONDITION" ] 6 # 在循环的顶部进行条件判断. 7 do 8 echo "Input variable #1 " 9 echo "($END_CONDITION to exit)" 10 read var1 11 echo "variable #1 = $var1" 12 echo 13 done 14 15 exit 0
嵌套循环就是在一个循环中还有一个循环, 内部循环在外部循环体中. 在外部循环的每次执行过程中都会触发内部循环, 直到内部循环执行结束. 外部循环执行了多少次, 内部循环就完成多少次. 当然, 无论是内部循环还是外部循环的break语句都会打断处理过程.
例子 15. 嵌套循环
1 #!/bin/bash 2 # nested-loop.sh: 嵌套的"for"循环. 3 4 outer=1 # 设置外部循环计数. 5 6 # 开始外部循环. 7 for a in 1 2 3 4 5 8 do 9 echo "Pass $outer in outer loop." 10 echo "---------------------" 11 inner=1 # 重置内部循环计数. 12 13 # =============================================== 14 # 开始内部循环. 15 for b in 1 2 3 4 5 16 do 17 echo "Pass $inner in inner loop." 18 let "inner+=1" # 增加内部循环计数. 19 done 20 # 内部循环结束. 21 # =============================================== 22 23 let "outer+=1" # 增加外部循环的计数. 24 echo # 每次外部循环之间的间隔. 25 done 26 # 外部循环结束. 27 28 exit 0
影响循环行为的命令
break, continue
break和continue这两个循环控制命令(这两个命令是shell的内建命令, 而不象其他的循环命令那样, 比如while和case, 这两个是关键字.)与其他语言的类似命令的行为是相同的. break命令用来跳出循环, 而continue命令只会跳过本次循环, 忽略本次循环剩余的代码, 进入循环的下一次迭代.
例子 16. break和continue命令在循环中的效果
1 #!/bin/bash
2
3 LIMIT=19 # 上限
4
5 echo
6 echo "Printing Numbers 1 through 20 (but not 3 and 11)."
7
8 a=0
9
10 while [ $a -le "$LIMIT" ]
11 do
12 a=$(($a+1))
13
14 if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # 除了3和11.
15 then
16 continue # 跳过本次循环剩余的语句.
17 fi
18
19 echo -n "$a " # 在$a等于3和11的时候,这句将不会执行.
20 done
21
22 # 练习:
23 # 为什么循环会打印出20?
24
25 echo; echo
26
27 echo Printing Numbers 1 through 20, but something happens after 2.
28
29 ##################################################################
30
31 # 同样的循环, 但是用'break'来代替'continue'.
32
33 a=0
34
35 while [ "$a" -le "$LIMIT" ]
36 do
37 a=$(($a+1))
38
39 if [ "$a" -gt 2 ]
40 then
41 break # 将会跳出整个循环.
42 fi
43
44 echo -n "$a "
45 done
46
47 echo; echo; echo
48
49 exit 0
break命令可以带一个参数. 一个不带参数的break命令只能退出最内层的循环, 而break N可以退出N层循环.
例子 17. 多层循环的退出
1 #!/bin/bash
2 # break-levels.sh: 退出循环.
3
4 # "break N" 退出N层循环.
5
6 for outerloop in 1 2 3 4 5
7 do
8 echo -n "Group $outerloop: "
9
10 # --------------------------------------------------------
11 for innerloop in 1 2 3 4 5
12 do
13 echo -n "$innerloop "
14
15 if [ "$innerloop" -eq 3 ]
16 then
17 break # 试试 break 2 来看看发生什么事.
18 # (内部循环和外部循环都被"Break"了. )
19 fi
20 done
21 # --------------------------------------------------------
22
23 echo
24 done
25
26 echo
27
28 exit 0
continue命令也可以象break命令一样带一个参数. 一个不带参数的continue命令只会去掉本次循环的剩余代码. 而continue N将会把N层循环的剩余代码都去掉, 但是循环的次数不变.
例子 18. 多层循环的continue
1 #!/bin/bash
2 # "continue N" 命令, 将让N层的循环全部被continue.
3
4 for outer in I II III IV V # 外部循环
5 do
6 echo; echo -n "Group $outer: "
7
8 # --------------------------------------------------------------------
9 for inner in 1 2 3 4 5 6 7 8 9 10 # 内部循环
10 do
11
12 if [ "$inner" -eq 7 ]
13 then
14 continue 2 # 在第2层循环上的continue, 也就是"外部循环".
15 # 使用"continue"来替代这句,
16 # 然后看一下一个正常循环的行为.
17 fi
18
19 echo -n "$inner " # 7 8 9 10 将不会被echo.
20 done
21 # --------------------------------------------------------------------
22 # 译者注: 如果在此处添加echo的话, 当然也不会输出.
23 done
24
25 echo; echo
26
27 # 练习:
28 # 在脚本中放入一个有意义的"continue N".
29
30 exit 0
continue N 结构如果用在有意义的场合中, 往往都很难理解, 并且技巧性很高. 所以最好的方法就是尽量避免使用它.
case 和 select 结构在技术上说并不是循环, 因为它们并不对可执行代码块进行迭代. 但是和循环相似的是, 它们也依靠在代码块顶部或底部的条件判断来决定程序的分支.
在代码块中控制程序分支
case (in) / esac
在shell中的case结构与C/C++中的switch结构是相同的. 它允许通过判断来选择代码块中多条路径中的一条. 它的作用和多个if/then/else语句的作用相同, 是它们的简化结构, 特别适用于创建菜单.
case "$variable" in
"$condition1" )
command...
;;
"$condition2" )
command...
;;
esac
例子 19. 使用case
1 #!/bin/bash 2 # 测试字符串范围. 3 4 echo; echo "Hit a key, then hit return." 5 read Keypress 6 7 case "$Keypress" in 8 [[:lower:]] ) echo "Lowercase letter";; 9 [[:upper:]] ) echo "Uppercase letter";; 10 [0-9] ) echo "Digit";; 11 * ) echo "Punctuation, whitespace, or other";; 12 esac # 允许字符串的范围出现在[中括号]中, 13 #+ 或者出现在POSIX风格的[[双中括号中. 14 15 # 在这个例子的第一个版本中, 16 #+ 测试大写和小写字符串的工作使用的是 17 #+ [a-z] 和 [A-Z]. 18 # 这种用法在某些特定场合的或某些Linux发行版中不能够正常工作. 19 # POSIX 的风格更具可移植性. 20 # 感谢Frank Wang指出了这点. 21 exit 0
例子 20. 使用case来创建菜单
1 #!/bin/bash 2 3 # 未经处理的地址资料 4 5 clear # 清屏. 6 7 echo " Contact List" 8 echo " ------- ----" 9 echo "Choose one of the following persons:" 10 echo 11 echo "[E]vans, Roland" 12 echo "[J]ones, Mildred" 13 echo "[S]mith, Julie" 14 echo "[Z]ane, Morris" 15 echo 16 17 read person 18 19 case "$person" in 20 # 注意, 变量是被""引用的. 21 22 "E" | "e" ) 23 # 接受大写或者小写输入. 24 echo 25 echo "Roland Evans" 26 echo "4321 Floppy Dr." 27 echo "Hardscrabble, CO 80753" 28 echo "(303) 734-9874" 29 echo "(303) 734-9892 fax" 30 echo "revans@zzy.net" 31 echo "Business partner & old friend" 32 ;; 33 # 注意, 每个选项后边都要以双分号;;结尾. 34 35 "J" | "j" ) 36 echo 37 echo "Mildred Jones" 38 echo "249 E. 7th St., Apt. 19" 39 echo "New York, NY 10009" 40 echo "(212) 533-2814" 41 echo "(212) 533-9972 fax" 42 echo "milliej@loisaida.com" 43 echo "Ex-girlfriend" 44 echo "Birthday: Feb. 11" 45 ;; 46 47 # 后边的 Smith 和 Zane 的信息在这里就省略了. 48 49 * ) 50 # 默认选项. 51 # 空输入(敲回车RETURN), 也适用于这里. 52 echo 53 echo "Not yet in database." 54 ;; 55 56 esac 57 58 echo 59 60 # 练习: 61 # ----- 62 # 修改这个脚本, 让它能够接受多个输入, 63 #+ 并且能够显示多个地址. 64 65 exit 0
例子 21. 使用命令替换来产生case变量
1 #!/bin/bash 2 # case-cmd.sh: 使用命令替换来产生"case"变量. 3 4 case $( arch ) in # "arch" 返回机器体系的类型.(me:一些系统中找不到这个命令或变量) 5 # 等价于 'uname -m' ... 6 i386 ) echo "80386-based machine";; 7 i486 ) echo "80486-based machine";; 8 i586 ) echo "Pentium-based machine";; 9 i686 ) echo "Pentium2+-based machine";; 10 * ) echo "Other type of machine";; 11 esac 12 13 exit 0
case结构也可以过滤通配(globbing)模式的字符串.
例子 22. 简单的字符串匹配
1 #!/bin/bash
2 # match-string.sh: 简单的字符串匹配
3
4 match_string ()
5 {
6 MATCH=0
7 NOMATCH=90
8 PARAMS=2 # 此函数需要2个参数.
9 BAD_PARAMS=91
10
11 [ $# -eq $PARAMS ] || return $BAD_PARAMS
12
13 case "$1" in
14 "$2") return $MATCH;;
15 * ) return $NOMATCH;;
16 esac
17
18 }
19
20
21 a=one
22 b=two
23 c=three
24 d=two
25
26
27 match_string $a # 参数个数错误.
28 echo $? # 91
29
30 match_string $a $b # 不匹配
31 echo $? # 90
32
33 match_string $b $d # 匹配
34 echo $? # 0
35
36
37 exit 0
select
select结构是建立菜单的另一种工具, 这种结构是从ksh中引入的.
select variable [in list]
do
command...
break
done
提示用户输入选择的内容(比如放在变量列表中). 注意: select命令使用PS3提示符, 默认为(#?), 当然, 这可以修改.
例子 23. 使用select来创建菜单
1 #!/bin/bash
2
3 PS3='Choose your favorite vegetable: ' # 设置提示符字串.
4
5 echo
6
7 select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"
8 do
9 echo
10 echo "Your favorite veggie is $vegetable."
11 echo
12 break # 如果这里没有 'break' 会发生什么?
13 done
14
15 exit 0
上面程序执行的情况如下:
$ ./select.sh 1) beans 2) carrots 3) potatoes 4) onions 5) rutabagas Choose your favorite vegetable: 1 # 注:输入的是 “beans” 前面的序号。如果这里输入的是 “beans”,情况会怎样? Your favorite veggie is beans.
如果忽略了in list列表, 那么select命令将会使用传递到脚本的命令行参数($@), 或者是函数参数(当select是在函数中时).
与忽略in list的
for variable [in list]
结构比较一下.
例子 24. 使用函数中的select结构来创建菜单
1 #!/bin/bash
2
3 PS3='Choose your favorite vegetable: '
4
5 echo
6
7 choice_of()
8 {
9 select vegetable
10 # [in list]被忽略, 所以'select'使用传递给函数的参数.
11 do
12 echo
13 echo "Your favorite veggie is $vegetable."
14 echo "Yuck!"
15 echo
16 break
17 done
18 }
19
20 choice_of beans rice carrots radishes tomatoes spinach
21 # $1 $2 $3 $4 $5 $6
22 # 传递给choice_of()的参数
23
24 exit 0
经典示例:使用间接变量引用的简单数据库应用(应用到“间接变量引用”和“select”结构)
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