# 20.1 使用 exec
一個 `exec < filename` 命令重定向了 `標準輸入` 到一個文件。自此所有 `標準輸入` 都來自該文件而不是默認來源(通常是鍵盤輸入)。在使用 [sed](http://tldp.org/LDP/abs/html/sedawk.html#SEDREF) 和 [awk](http://tldp.org/LDP/abs/html/awk.html#AWKREF) 時候這種方式可以逐行讀文件并逐行解析。
樣例 20-1. 使用 `exec` 重定向 標準輸入
```
#!/bin/bash
# 使用 'exec' 重定向 標準輸入 .
exec 6<&0 # 鏈接文件描述符 #6 到標準輸入.
# .
exec < data-file # 標準輸入被文件 "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<&-
# 現在在之前保存的位置將從文件描述符 #6 將 標準輸出 恢復.
#+ 且關閉文件描述符 #6 ( 6<&- ) 讓其他程序正常使用.
#
# <&6 6<&- also works.
echo -n "Enter data "
read b1 # 現在按預期的,從正常的標準輸入 "read".
echo "Input read from stdin."
echo "----------------------"
echo "b1 = $b1"
echo
exit 0
```
同理, `exec >filename` 重定向 標準輸出 到指定文件. 他將所有的命令輸出通常是 標準輸出 重定向到指定的位置.
`exec N > filename` 影響整個腳本或當前 shell。[PID](http://tldp.org/LDP/abs/html/special-chars.html#PROCESSIDREF) 從重定向腳本或 shell 的那時候已經發生了改變. 然而 `N > filename` 影響的就是新派生的進程,而不是整個腳本或 shell。
樣例 20-2. 使用 exec 重定向標準輸出
```
#!/bin/bash
# reassign-stdout.sh
LOGFILE=logfile.txt
exec 6>&1 # 鏈接文件描述符 #6 到標準輸出.
# 保存標準輸出.
exec > $LOGFILE # 標準輸出被文件 "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>&- # 關閉文件描述符 #6 恢復 標準輸出.
echo
echo "== stdout now restored to default == "
echo
ls -al
echo
exit 0
```
樣例 20-3. 用 exec 在一個腳本里同時重定向 標準輸入 和 標準輸出
```
#!/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 # 轉化大寫.
# ^^^^^ # 讀取標準輸入.
# ^^^^^^^^^^ # 寫到標準輸出.
# 然而標準輸入和標準輸出都會被重定向.
# 注意 'cat' 可能會被遺漏.
# -----------------------------------------------
exec 1>&7 7>&- # 恢復標準輸出.
exec 0<&4 4<&- # 恢復標準輸入.
# 恢復后, 下面這行會預期從標準輸出打印.
echo "File \"$1\" written to \"$2\" as uppercase conversion."
exit 0
```
I/O 重定向是種明智的規避 [inaccessible variables within a subshell](http://tldp.org/LDP/abs/html/subshells.html#PARVIS) 問題的方法.
樣例 20-4. 規避子 shell
```
#!/bin/bash
# avoid-subshell.sh
# Matthew Walker 的建議.
Lines=0
echo
cat myfile.txt | while read line;
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.
```
- 第一部分 初見shell
- 1. 為什么使用shell編程
- 2. 和Sha-Bang(#!)一起出發
- 2.1 調用一個腳本
- 2.2 牛刀小試
- 第二部分 shell基礎
- 3. 特殊字符
- 4. 變量與參數
- 4.1 變量替換
- 4.2 變量賦值
- 4.3 Bash弱類型變量
- 4.4 特殊變量類型
- 5. 引用
- 5.1 引用變量
- 5.2 轉義
- 6. 退出與退出狀態
- 7. 測試
- 7.1 測試結構
- 7.2 文件測試操作
- 7.3 其他比較操作
- 7.4 嵌套 if/then 條件測試
- 7.5 牛刀小試
- 8. 運算符相關話題
- 8.1 運算符
- 8.2 數字常量
- 8.3 雙圓括號結構
- 8.4 運算符優先級
- 第三部分 shell進階
- 10. 變量處理
- 10.1 字符串處理
- 10.1.1 使用 awk 處理字符串
- 10.1.2 參考資料
- 10.2 參數替換
- 11. 循環與分支
- 11.1 循環
- 11.2 嵌套循環
- 11.3 循環控制
- 11.4 測試與分支
- 12. 命令替換
- 13. 算術擴展
- 14. 休息時間
- 第五部分 進階話題
- 19. 嵌入文檔
- 20. I/O 重定向
- 20.1 使用 exec
- 20.2 重定向代碼塊
- 20.3 應用程序
- 22. 限制模式的Shell
- 23. 進程替換
- 26. 列表結構
- 25. 別名