# 20 I/O 重定向
目錄
- [20.1 使用 exec](http://tldp.org/LDP/abs/html/x17974.html)
- [20.2 重定向代碼塊](http://tldp.org/LDP/abs/html/redircb.html)
- [20.3 應用程序](http://tldp.org/LDP/abs/html/redirapps.html)
有三個默認打開的文件[[1]](http://tldp.org/LDP/abs/html/io-redirection.html#FTN.AEN17884), `stdin`(標準輸入,鍵盤),`stdout`(標準輸出, 屏幕)和 `stderr`(標準錯誤,屏幕上輸出的錯誤信息)。這些和任何其他打開的文件都可以被重定向。重定向僅僅意味著捕獲輸出文件,命令,腳本,甚至是一個腳本的代碼塊([樣例 3-1](http://tldp.org/LDP/abs/html/special-chars.html#EX8))和([樣例 3-2](http://tldp.org/LDP/abs/html/special-chars.html#EX8)) 作為另一個文件,命令,程序或腳本的輸入。
每個打開的文件都有特定的文件描述符。[[2]](http://tldp.org/LDP/abs/html/io-redirection.html#FTN.AEN17894),而 `stdin`,`stdout`,`stderr` 的文件描述符分別為 0,1,2。當然了,還有附件的文件描述符 3 - 9。有時候為`stdin`,`stdout`,`stderr`臨時性的復制鏈接分配這些附加的文件描述符會非常有用.[[3]](http://tldp.org/LDP/abs/html/io-redirection.html#FTN.AEN17906)。這簡化了復雜重定向和重組后的恢復(見[樣例 20-1](http://tldp.org/LDP/abs/html/x17974.html#REDIR1))
```
COMMAND_OUTPUT >
# 重定向標準輸出到一個文件.
# 如果文件不存在則創建,否則覆蓋.
ls -lR > dir-tree.list
# 創建了一個包含目錄樹列表的文件.
: > filename
# ">" 清空了文件.
# 如果文件不存在,則創建了個空文件 (效果類似 'touch').
# ":" 是個虛擬占位符, 不會有輸出.
> filename
# ">" 清空了文件.
# 如果文件不存在,則創建了個空文件 (效果類似 'touch').
# (結果和上述的 ": >" 一樣, 但在某些 shell 環境中不能正常運行.)
COMMAND_OUTPUT >>
# 重定向標準輸出到一個文件.
# 如果文件不存在則創建,否則新內容在文件末尾追加.
# 單行重定向命令 (只作用于本身所在的那行):
# --------------------------------------------------------------------
1>filename
# 以覆蓋的方式將 標準錯誤 重定向到文件 "filename."
1>>filename
# 以追加的方式將 標準輸出 重定向到文件 "filename."
2>filename
# 以覆蓋的方式將 標準錯誤 重定向到文件 "filename."
2>>filename
# 以追加的方式將 標準錯誤 重定向到文件 "filename."
&>filename
# 以覆蓋的方式將 標準錯誤 和 標準輸出 同時重定向到文件 "filename."
# 在 bash 4 中才有這個新功能.
M>N
# "M" 是個文件描述符, 如果不明確指定,默認為 1.
# "N" 是個文件名.
# 文件描述符 "M" 重定向到文件 "N."
M>&N
# "M" 是個文件描述符, 如果不設置默認為 1.
# "N" 是另一個文件描述符.
#==============================================================================
# 重定向 標準輸出,一次一行.
LOGFILE=script.log
echo "This statement is sent to the log file, \"$LOGFILE\"." 1>$LOGFILE
echo "This statement is appended to \"$LOGFILE\"." 1>>$LOGFILE
echo "This statement is also appended to \"$LOGFILE\"." 1>>$LOGFILE
echo "This statement is echoed to stdout, and will not appear in \"$LOGFILE\"."
# 這些重定向命令在每行結束后自動"重置".
# 重定向 標準錯誤,一次一行.
ERRORFILE=script.errors
bad_command1 2>$ERRORFILE # Error message sent to $ERRORFILE.
bad_command2 2>>$ERRORFILE # Error message appended to $ERRORFILE.
bad_command3 # Error message echoed to stderr,
#+ and does not appear in $ERRORFILE.
# 這些重定向命令每行結束后會自動“重置”.
#=======================================================================
```
```
2>&1
# 重定向 標準錯誤 到 標準輸出.
# 錯誤信息發送到標準輸出相同的位置.
>>filename 2>&1
bad_command >>filename 2>&1
# 同時將 標準輸出 和 標準錯誤 追加到文件 "filename" 中 ...
2>&1 | [command(s)]
bad_command 2>&1 | awk '{print $5}' # found
# 通過管道傳遞 標準錯誤.
# bash 4 中可以將 "2>&1 |" 縮寫為 "|&".
i>&j
# 重定向文件描述符 i 到 j.
# 文件描述符 i 指向的文件輸出將會重定向到文件描述符 j 指向的文件
>&j
# 默認的標準輸出 (stdout) 重定向到 j.
# 所有的標準輸出將會重定向到 j 指向的文件.
```
```
0< FILENAME
< FILENAME
# 從文件接收輸入.
# 類似功能命令是 ">", 經常會組合使用.
#
# grep search-word <filename
[j]<>filename
# 打開并讀寫文件 "filename" ,
#+ 并且分配文件描述符 "j".
# 如果 "filename" 不存在則創建.
# 如果文件描述符 "j" 未指定, 默認分配文件描述符 0, 標準輸入.
#
# 這是一個寫指定文件位置的應用程序.
echo 1234567890 > File # 寫字符串到 "File".
exec 3<> File # 打開并分配文件描述符 3 給 "File" .
read -n 4 <&3 # 讀取 4 字符.
echo -n . >&3 # 寫一個小數點.
exec 3>&- # 關閉文件描述符 3.
cat File # ==> 1234.67890
# 隨機訪問.
|
# 管道.
# 一般是命令和進程的鏈接工具.
# 類似 ">", 但更一般.
# 在連接命令,腳本,文件和程序方面非常有用.
cat *.txt | sort | uniq > result-file
# 所有 .txt 文件輸出進行排序并且刪除復制行,
# 最終保存結果到 "result-file".
```
可以用單個命令行表示輸入和輸出的多個重定向或管道.
```
command < input-file > output-file
# 或者等價:
< input-file command > output-file # 盡管這不標準.
command1 | command2 | command3 > output-file
```
更多詳情見[樣例 16-31](http://tldp.org/LDP/abs/html/filearchiv.html#DERPM) and [樣例 A-14](http://tldp.org/LDP/abs/html/contributed-scripts.html#FIFO).
多個輸出流可以重定向到一個文件.
```
ls -yz >> command.log 2>&1
# 捕獲不合法選項 "yz" 的結果到文件 "command.log."
# 因為 標準錯誤輸出 被重定向到了文件,
#+ 任何錯誤信息都會在這.
# 注意, 然而, 接下來的這個案例并 "不能" 同樣的結果.
ls -yz 2>&1 >> command.log
# 輸出一條錯誤信息,但是不會寫入到文件.
# 恰恰的, 命令輸出(這個例子里為空)寫入到文件, 但錯誤信息只會在 標準輸出 輸出.
# 如果同時重定向 標準輸出 和 標準錯誤輸出,
#+ 命令的順序不同會導致不同.
```
關閉文件描述符
```
n<&-
關閉輸入文件描述符 n.
0<&-, <&-
關閉標準輸入.
n>&-
關閉輸出文件描述符 n.
1>&-, >&-
關閉標準輸出.
```
子進程能繼承文件描述符.這就是管道符能工作的原因.通過關閉文件描述符來防止繼承 .
```
# 只重定向到 標準錯誤 到管道.
exec 3>&1 # 保存當前 標準輸出 "值".
ls -l 2>&1 >&3 3>&- | grep bad 3>&- # 關閉 'grep' 文件描述符 3 (但不是 'ls').
# ^^^^ ^^^^
exec 3>&- # 現在關閉它.
# 感謝, S.C.
```
更多關于 I/O 重定向詳情見 [Appendix F](http://tldp.org/LDP/abs/html/ioredirintro.html).
#### 注意
[[1]](http://tldp.org/LDP/abs/html/io-redirection.html#AEN17884) 在 UNIX 和 Linux 中, 數據流和周邊外設([device files](http://tldp.org/LDP/abs/html/devref1.html#DEVFILEREF)) 都被看做文件.
[[2]](http://tldp.org/LDP/abs/html/io-redirection.html#AEN17894) `文件描述符` 僅僅是操作系統分配的一個可追蹤的打開的文件號. 可以認為是一個簡化的文件指針. 類似于 C 語言的 `文件句柄`.
[[3]](http://tldp.org/LDP/abs/html/io-redirection.html#AEN17906) 當 bash 創建一個子進程的時候使用 `文件描述符 5` 會有問題. 例如 [exec](http://tldp.org/LDP/abs/html/internal.html#EXECREF), 子進程繼承了文件描述符 5 (詳情見 Chet Ramey's 歸檔的 e-mail, [SUBJECT: RE: File descriptor 5 is held open](https://groups.google.com/forum/#!topic/gnu.bash.bug/E5Vdqv3tO1w)). 最好將這個文件描述符單獨規避.
- 第一部分 初見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. 別名