20.1 使用 exec
一个 exec < filename 命令重定向了 标准输入 到一个文件。自此所有 标准输入 都来自该文件而不是默认来源(通常是键盘输入)。在使用 sed 和 awk 时候这种方式可以逐行读文件并逐行解析。
样例 20-1. 使用 exec 重定向 标准输入
1. #!/bin/bash
2. # 使用 'exec' 重定向 标准输入 .
5. exec 6<&0 # 链接文件描述符 #6 到标准输入.
6. # .
8. exec < data-file # 标准输入被文件 "data-file" 替换
10. read a1 # 读取文件 "data-file" 首行.
11. read a2 # 读取文件 "data-file" 第二行
13. echo
14. echo "Following lines read from file."
15. echo "-------------------------------"
16. echo $a1
17. echo $a2
19. echo; echo; echo
21. exec 0<&6 6<&-
22. # 现在在之前保存的位置将从文件描述符 #6 将 标准输出 恢复.
23. #+ 且关闭文件描述符 #6 ( 6<&- ) 让其他程序正常使用.
24. #
25. # <&6 6<&- also works.
27. echo -n "Enter data "
28. read b1 # 现在按预期的,从正常的标准输入 "read".
29. echo "Input read from stdin."
30. echo "----------------------"
31. echo "b1 = $b1"
33. echo
35. exit 0
同理, exec >filename 重定向 标准输出 到指定文件. 他将所有的命令输出通常是 标准输出 重定向到指定的位置.
exec N > filename 影响整个脚本或当前 shell。PID 从重定向脚本或 shell 的那时候已经发生了改变. 然而 N > filename 影响的就是新派生的进程,而不是整个脚本或 shell。
样例 20-2. 使用 exec 重定向标准输出
1. #!/bin/bash
2. # reassign-stdout.sh
4. LOGFILE=logfile.txt
6. exec 6>&1 # 链接文件描述符 #6 到标准输出.
7. # 保存标准输出.
9. exec > $LOGFILE # 标准输出被文件 "logfile.txt" 替换.
11. # ----------------------------------------------------------- #
12. # 所有在这个块里的命令的输出都会发送到文件 $LOGFILE.
14. echo -n "Logfile: "
15. date
16. echo "-------------------------------------"
17. echo
19. echo "Output of \"ls -al\" command"
20. echo
21. ls -al
22. echo; echo
23. echo "Output of \"df\" command"
24. echo
25. df
27. # ----------------------------------------------------------- #
29. exec 1>&6 6>&- # 关闭文件描述符 #6 恢复 标准输出.
31. echo
32. echo "== stdout now restored to default == "
33. echo
34. ls -al
35. echo
37. exit 0
样例 20-3. 用 exec 在一个脚本里同时重定向 标准输入 和 标准输出
1. #!/bin/bash
2. # upperconv.sh
3. # 转化指定的输入文件成大写.
5. E_FILE_ACCESS=70
6. E_WRONG_ARGS=71
8. if [ ! -r "$1" ] # 指定的输入文件是否可读?
9. then
10. echo "Can't read from input file!"
11. echo "Usage: $0 input-file output-file"
12. exit $E_FILE_ACCESS
13. fi # 同样的错误退出
14. #+ 等同如果输入文件 ($1) 未指定 (为什么?).
16. if [ -z "$2" ]
17. then
18. echo "Need to specify output file."
19. echo "Usage: $0 input-file output-file"
20. exit $E_WRONG_ARGS
21. fi
24. exec 4<&0
25. exec < $1 # 将从输入文件读取.
27. exec 7>&1
28. exec > $2 # 将写入输出文件.
29. # 假定输出文件可写 (增加检测?).
31. # -----------------------------------------------
32. cat - | tr a-z A-Z # 转化大写.
33. # ^^^^^ # 读取标准输入.
34. # ^^^^^^^^^^ # 写到标准输出.
35. # 然而标准输入和标准输出都会被重定向.
36. # 注意 'cat' 可能会被遗漏.
37. # -----------------------------------------------
39. exec 1>&7 7>&- # 恢复标准输出.
40. exec 0<&4 4<&- # 恢复标准输入.
42. # 恢复后, 下面这行会预期从标准输出打印.
43. echo "File \"$1\" written to \"$2\" as uppercase conversion."
45. exit 0
I/O 重定向是种明智的规避 inaccessible variables within a subshell 问题的方法.
样例 20-4. 规避子 shell
1. #!/bin/bash
2. # avoid-subshell.sh
3. # Matthew Walker 的建议.
5. Lines=0
7. echo
9. cat myfile.txt | while read line;
10. do {
11. echo $line
12. (( Lines++ )); # 递增变量的值趋近外层循环
13. # 使用子 shell 会有问题.
14. }
15. done
17. echo "Number of lines read = $Lines" # 0
18. # 报错!
20. echo "------------------------"
23. exec 3<> myfile.txt
24. while read line <&3
25. do {
26. echo "$line"
27. (( Lines++ )); # 递增变量的值趋近外层循环.
28. # 没有子 shell,就不会有问题.
29. }
30. done
31. exec 3>&-
33. echo "Number of lines read = $Lines" # 8
35. echo
37. exit 0
39. # 下面的行并不在脚本里.
41. $ cat myfile.txt
43. Line 1.
44. Line 2.
45. Line 3.
46. Line 4.
47. Line 5.
48. Line 6.
49. Line 7.
50. Line 8.
