exec <filename命令会将stdin重定向到文件中. 从这句开始, 所有的stdin就都来自于这个文件了, 而不是标准输入(通常都是键盘输入). 这样就提供了一种按行读取文件的方法, 并且可以使用sed和/或awk来对每一行进行分析.
例子 16-1. 使用exec重定向stdin
#!/bin/bash # 使用'exec'重定向stdin. exec 6<&0 # 将文件描述符#6与stdin链接起来. # 保存stdin. exec < data-file # stdin被文件"data-file"所代替. read a1 # 读取文件"data-file"的第一行. read a2 # 读取文件"data-file"的第二行. echo echo "Following lines read from file." echo "-------------------------------" echo $a1 echo $a2 echo; echo; echo exec 0<&6 6<&- # 现在将stdin从fd #6中恢复, 因为刚才我们把stdin重定向到#6了, #+ 然后关闭fd #6 ( 6<&- ), 好让这个描述符继续被其他进程所使用. # # <&6 6<&- 这么做也可以. echo -n "Enter data " read b1 # 现在"read"已经恢复正常了, 就是能够正常的从stdin中读取. echo "Input read from stdin." echo "----------------------" echo "b1 = $b1" echo exit 0 |
同样的, exec >filename命令将会把stdout重定向到一个指定的文件中. 这样所有命令的输出就都会发送到那个指定的文件, 而不是stdout.
![]() | exec N > filename会影响整个脚本或当前shell. 对于这个指定PID的脚本或shell来说, 从这句命令执行之后, 就会重定向到这个文件中, 然而 . . . N > filename只会影响新fork出来的进程, 而不会影响整个脚本或shell. not the entire script or shell. 感谢你, Ahmed Darwish, 指出这个问题. |
例子 16-2. 使用exec来重定向stdout
#!/bin/bash # reassign-stdout.sh LOGFILE=logfile.txt exec 6>&1 # 将fd #6与stdout链接起来. # 保存stdout. exec > $LOGFILE # stdout就被文件"logfile.txt"所代替了. # ----------------------------------------------------------- # # 在这块中所有命令的输出都会发送到文件$LOGFILE中. echo -n "Logfile: " date echo "-------------------------------------" echo echo "Output of \"ls -al\" command" echo ls -al echo; echo echo "Output of \"df\" command" echo df # ----------------------------------------------------------- # exec 1>&6 6>&- # 恢复stdout, 然后关闭文件描述符#6. echo echo "== stdout now restored to default == " echo ls -al echo exit 0 |
例子 16-3. 使用exec在同一个脚本中重定向stdin和stdout
#!/bin/bash # upperconv.sh # 将一个指定的输入文件转换为大写. E_FILE_ACCESS=70 E_WRONG_ARGS=71 if [ ! -r "$1" ] # 判断指定的输入文件是否可读? then echo "Can't read from input file!" echo "Usage: $0 input-file output-file" exit $E_FILE_ACCESS fi # 即使输入文件($1)没被指定 #+ 也还是会以相同的错误退出(为什么?). if [ -z "$2" ] then echo "Need to specify output file." echo "Usage: $0 input-file output-file" exit $E_WRONG_ARGS fi exec 4<&0 exec < $1 # 将会从输入文件中读取. exec 7>&1 exec > $2 # 将写到输出文件中. # 假设输出文件是可写的(添加检查?). # ----------------------------------------------- cat - | tr a-z A-Z # 转换为大写. # ^^^^^ # 从stdin中读取. # ^^^^^^^^^^ # 写到stdout上. # 然而, stdin和stdout都被重定向了. # ----------------------------------------------- exec 1>&7 7>&- # 恢复stout. exec 0<&4 4<&- # 恢复stdin. # 恢复之后, 下边这行代码将会如预期的一样打印到stdout上. echo "File \"$1\" written to \"$2\" as uppercase conversion." exit 0 |
I/O重定向是一种避免可怕的子shell中不可访问变量问题的方法.
例子 16-4. 避免子shell
#!/bin/bash # avoid-subshell.sh # 由Matthew Walker所提出的建议. Lines=0 echo cat myfile.txt | while read line; # (译者注: 管道会产生子shell) do { echo $line (( Lines++ )); # 增加这个变量的值 #+ 但是外部循环却不能访问. # 子shell问题. } done echo "Number of lines read = $Lines" # 0 # 错误! echo "------------------------" exec 3<> myfile.txt while read line <&3 do { echo "$line" (( Lines++ )); # 增加这个变量的值 #+ 现在外部循环就可以访问了. # 没有子shell, 现在就没问题了. } done exec 3>&- echo "Number of lines read = $Lines" # 8 echo exit 0 # 下边这些行是这个脚本的结果, 脚本是不会走到这里的. $ cat myfile.txt Line 1. Line 2. Line 3. Line 4. Line 5. Line 6. Line 7. Line 8. |