| 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 是重定向代码块的一个特例.