Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting | ||
---|---|---|
Prev | Chapter 16. I/O Redirection | Next |
像 while, until, 和 for 循环代码块, 甚至 if/then 测试结构的代码块都能做到标准输入的重定向. 即使函数也可以使用这种重定向的格式 (参考例子 23-11). 所有的这些依靠代码块结尾的 < 操作符来做到.
例子 16-5. while 循环的重定向
1 #!/bin/bash 2 # redir2.sh 3 4 if [ -z "$1" ] 5 then 6 Filename=names.data # 如果没有指定文件名,则指定这个默认值. 7 else 8 Filename=$1 9 fi 10 #+ Filename=${1:-names.data} 11 # 这句可代替上面的测试 (参数替换). 12 13 count=0 14 15 echo 16 17 while [ "$name" != Smith ] # 为什么变量 $name 要用引号? 18 do 19 read name # 从$Filename文件中读而非在标准输入中读. 20 echo $name 21 let "count += 1" 22 done <"$Filename" # 重定向标准输入到文件$Filename. 23 # ^^^^^^^^^^^^ 24 25 echo; echo "$count names read"; echo 26 27 exit 0 28 29 # 注意在老一些的shell脚本编程语言里, 30 #+ 重定向的循环是在子shell里运行的. 31 # 因此, $count 值返回后会是 0, 此值是在循环开始前的值. 32 # Bash 和 ksh 只要可能就避免启用子shell, 33 #+ 因此这个例子能正确运行. 34 # (多谢Heiner Steven指出这个问题.) 35 36 # 然而 . . . 37 # Bash有时仍会启用一个子shell来处理重定向的"while"循环. 38 39 abc=hi 40 echo -e "1\n2\n3" | while read l 41 do abc="$l" 42 echo $abc 43 done 44 echo $abc 45 46 # (多谢Bruno de Oliveira Schneider给出上面的代码片段来演示此问题.) 47 # |
例子 16-6. 重定向while 循环的另一种形式
1 #!/bin/bash 2 3 # 这是前个脚本的另一个版本. 4 5 # Heiner Steven谈到 6 #+ 在重定向循环时会以子Shell运行的环境里, 7 #+ 循环内的值在循环结束后不会保持循环内的值. 8 # 9 10 11 if [ -z "$1" ] 12 then 13 Filename=names.data # 如果没有指定文件名则使用默认值. 14 else 15 Filename=$1 16 fi 17 18 19 exec 3<&0 # 把标准输入关联到文件描述符. 20 exec 0<"$Filename" # 重定向标准输入. 21 22 count=0 23 echo 24 25 26 while [ "$name" != Smith ] 27 do 28 read name # 从标准输入($Filename)中读. 29 echo $name 30 let "count += 1" 31 done # 从文件$Filename中循环的读 32 #+ 因为文件(译者注:指默认的文件,在这节最后面附上)有20行. 33 34 # 这个脚本原先在"while"循环的结尾是用: 35 #+ done <"$Filename" 36 # 练习: 37 # 为什么这是不必要的? 38 39 40 exec 0<&3 # 恢复旧的标准输入. 41 exec 3<&- # 关闭临时文件描述符3. 42 43 echo; echo "$count names read"; echo 44 45 exit 0 |
例子 16-7. 重定向 until 循环
1 #!/bin/bash 2 # 和前面的例子相同, 但使用的是"until"循环. 3 4 if [ -z "$1" ] 5 then 6 Filename=names.data # 如果没有指定文件名使用默认值. 7 else 8 Filename=$1 9 fi 10 11 # while [ "$name" != Smith ] 12 until [ "$name" = Smith ] # 把 != 改为 =. 13 do 14 read name # 从文件$Filename中读而非从标准输入中读. 15 echo $name 16 done <"$Filename" # 重定向标准输入到文件$Filename. 17 # ^^^^^^^^^^^^ 18 19 # 结果和前面的"while"循环例子相同. 20 21 exit 0 |
例子 16-8. 重定向 for 循环
1 #!/bin/bash 2 3 if [ -z "$1" ] 4 then 5 Filename=names.data # 如果没有指定文件名就使用默认值. 6 else 7 Filename=$1 8 fi 9 10 line_count=`wc $Filename | awk '{ print $1 }'` 11 # 目标文件的行数. 12 # 13 # 代码非常的刻意和难看,但至少展示了for循环的标准输入可以重定向... 14 #+ 当然你要足够聪明能看出来. 15 # 16 # 17 # 更简洁的办法是 line_count=$(wc -l < "$Filename") 18 19 20 for name in `seq $line_count` # 调用 "seq" 来打印数字序列. 21 # while [ "$name" != Smith ] -- for循环比单个"while"循环更复杂 -- 22 do 23 read name # 从$Filename文件而非标准输入读. 24 echo $name 25 if [ "$name" = Smith ] # 因为用for循环,所以需要这个累赘测试. 26 then 27 break 28 fi 29 done <"$Filename" # 重定向标准输入到文件 $Filename. 30 # ^^^^^^^^^^^^ 31 32 exit 0 |
我们也可以修改前面的例子使其能重定向循环的标准输出.
例子 16-9. 重定向 for 循环 (标准输入和标准输出都做重定向)
1 #!/bin/bash 2 3 if [ -z "$1" ] 4 then 5 Filename=names.data # 如果没有指定文件名,则使用默认值. 6 else 7 Filename=$1 8 fi 9 10 Savefile=$Filename.new # 保存结果的文件名. 11 FinalName=Jonah # 终止"read"时的名称. 12 13 line_count=`wc $Filename | awk '{ print $1 }'` # 目标文件的行数. 14 15 16 for name in `seq $line_count` 17 do 18 read name 19 echo "$name" 20 if [ "$name" = "$FinalName" ] 21 then 22 break 23 fi 24 done < "$Filename" > "$Savefile" # 重定向标准输出到文件 $Filename, 25 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 并保存输出到备份文件中. 26 27 exit 0 |
例子 16-10. 重定向 if/then 测试结构
1 #!/bin/bash 2 3 if [ -z "$1" ] 4 then 5 Filename=names.data # 如果文件名没有指定,使用默认值. 6 else 7 Filename=$1 8 fi 9 10 TRUE=1 11 12 if [ "$TRUE" ] # if true 和 if : 都可以. 13 then 14 read name 15 echo $name 16 fi <"$Filename" 17 # ^^^^^^^^^^^^ 18 19 # 只读了文件的第一行. 20 # "if/then"测试结构不会自动地反复地执行,除非把它们嵌到循环里. 21 22 exit 0 |
例子 16-11. 用于上面例子的"names.data"数据文件
1 Aristotle 2 Belisarius 3 Capablanca 4 Euler 5 Goethe 6 Hamurabi 7 Jonah 8 Laplace 9 Maroczy 10 Purcell 11 Schmidt 12 Semmelweiss 13 Smith 14 Turing 15 Venn 16 Wilson 17 Znosko-Borowski 18 19 # 此数据文件用于: 20 #+ "redir2.sh", "redir3.sh", "redir4.sh", "redir4a.sh", "redir5.sh". |
重定向代码块的标准输出有保存它的输出到文件中的作用. 参考例子 3-2.
Here documents 是重定向代码块的一个特例.