這是翻譯系列文章的第3篇,參考原文連結 。
翻譯的系列文章列表:
- Bash 單命令列解釋,第一部分:檔案操作
- Bash 單命令列解釋,第二部分:字串操作
- Bash 單命令列解釋,第三部分:重定向(本篇)
這是 Bash One-Liners Explained 系列文章的第3篇。本文中將講授 輸入/輸出重定向 。我將利用 bash 最佳實踐,多變的 bash 方言和 bash 命令技巧,展示僅利用 bash 內建命令和外部程式命令構建命令流完成各種各樣的任務需求。
參見系列文章的第一篇 Bash 單命令列解釋,第一部分:檔案操作 瞭解我的初衷。若這個系列文章完成後,我將放出其同名的電子書。正如我創作的其它系列文章-- awk,sed 和 perl 一樣,我的電子書 有 pdf 格式及方便在手機上看的格式(mobi 和 epub)。同時,像 perl1line.txt 一樣,也有純文字 txt 格式的文件。
也可參考我其它的高效使用 bash 的系列文章:
- Working Productively in Bash's Emacs Command Line Editing Mode (comes with a cheat sheet)
- Working Productively in Bash's Vi Command Line Editing Mode (comes with a cheat sheet)
- The Definitive Guide to Bash Command Line History (comes with a cheat sheet)
開始學習
第三部分:重定向
當你理解了 檔案描述符(file descriptors) 的操作,將會覺得使用 bash 的重定向非常簡單。當 bash 啟動時,它將預設開啟 3 個標準檔案描述符 :stdin(0號檔案描述符) ,stdout(1號檔案描述符) ,stderr(2號檔案描述符) 。你可以開啟更多的檔案描述符(諸如:3,4,5 ...),也可以關閉它們,你可以拷貝檔案描述符 ,也可以從它那裡讀或寫入到它。
檔案描述符 總是指向某些檔案(除非檔案被關閉)。通常,bash 啟動時會把3個標準檔案描述符:stdin,stdout,stderr 通通指向終端。輸入(stdin)來自終端的鍵入內容,所有的輸出(stdout,stderr)顯示在終端裡。
假定你的終端是 /dev/tty0 ,下面是終端與標準檔案描述符之間的關係示意圖:
當 Bash 執行一條命令時,它會 分支(fork) 一個子程式,這個子程式繼承 fork 它的父程式的一切標準檔案描述符(關於 fork ,詳情參考手冊命令 man 2 fork
)。首先按照命令列指示進行 重定向輸入/輸出 後才 執行(exec) 命令(關於 exec ,詳情參考手冊命令 man 3 exec
)。
對於想 bash 重定向 操作進階的你,永遠心中有個藍圖 -- 當重定向發生時,標準檔案描述符如何變化 --,這個藍圖將極大地幫助到你。
1. 重定向標準輸出到一個檔案
$ command >file
符號 > 是輸出重定向操作符。bash 先嚐試開啟重定向指向的檔案(這裡是檔案 file)並可寫入,如果成功,Bash 將 command
命令子程式的標準輸出檔案描述符指向開啟的檔案。如果失敗,整個命令失敗退出,結束子程式。
命令列 command >file
等同於命令列 command 1>file
。數字 1 代表 stdout,它是標準輸出檔案描述符 stdout 的檔案描述符號。
下面是標準檔案描述符改變的藍圖示意。Bash 開啟檔案 file 並確定可寫入後,替換子程式的標準輸出檔案描述符1為 file 檔案的檔案描述符,自此,命令的所有輸出將寫入檔案 file 中。
通常,你可使用形如 "command n>file" 的命令,表示此命令的 n 號標準輸入/輸出檔案描述符指向 file 。
舉例:
$ ls > file_list
重定向 ls
命令的輸出到 file_list 檔案(命令的輸出寫入到檔案)。
2. 重定向命令的標準錯誤輸出到檔案
$ command 2> file
這裡,將命令的標準錯誤輸出(stderr 號 2)檔案描述符重定向到檔案。這裡的數字 2 代表標準錯誤輸出檔案描述符。
下面是標準檔案描述符表改變藍圖:
Bash 開啟檔案 file 並確定可寫入後,替換子程式的標準錯誤輸出檔案描述符2為 file 檔案的檔案描述符,自此,命令的所有錯誤輸出由原來寫入 stderr 中改寫入檔案 file 中。
3. 重定向標準輸出和標準錯誤輸出到檔案
$ command &>file
上面這行命令中 "&>" 表示把所有的標準輸出檔案描述符(stdout 和 stderr )重定向到 file 檔案描述符。&> 是 Bash 快速引用所有標準輸出描述符(1 和 2)的簡寫。
下圖是標準檔案描述符改變示意圖:
這裡你看到所有的標準輸出( stdout 和 stderr )都指向 file 檔案的檔案描述符。
有不同的幾個方法將所有標準輸出重定向到一個目標。可以按順序一個接一個地重定向:
$ command >file 2>&1
上面命令是較通用的重定向所有標準輸出到檔案的方法。首先,標準輸出(stdout)重定向到 file 檔案(這步是先執行 ">file" 的結果)。之後,標準錯誤輸出(stderr)重定向到 已重定向過的 stdout(&1) 上 (通過 "2>&1") 。因此,所有的標準輸出都重定向到 file 上。
Bash 在命令列解析時,發現有多個重定向操作時,按從左到右的順序依次解釋處理。讓我們按 Bash 執行順序,單步分析,更好地理解它。一開始,Bash 在執行命令前,其3個標準輸入/輸出檔案描述符都指向終端。示意圖如下:
首先,Bash 處理第一個重定向 ">file" 。之前,我們見過這個示意圖。它的標準輸出(stdout)指向終端:
下一步解釋處理重定向操作 "2>&1",這個操作的含義是使標準錯誤輸出檔案描述符(stderr 號 2)成為 1號標準輸出檔案描述符(stdout)的副本(重定向 2 到 1 的當前引用)。我們得到如下示意圖:
所有的標準輸出描述符都重定向到 file 檔案。
然而,小心不要把 command >file 2>&1
寫成 command 2>&1 >file
,它們是不同的!
Bash 對重定向指令的順序敏感!後一命令僅僅把標準輸出1(stdout) 重定向到 file 檔案,而 標準輸出2(stderr) 仍然輸出到終端。理解為什麼,讓我們再進行單步分析,看看發生了什麼。初始示意圖如下:
Bash 從左到右一步步處理重定向,首先遇到 "2>&1" ,因此將 2 (stderr) 重定向與 1(stdout) 相同,即都指向終端,示意圖如下:
現在,Bash 看到第2個重定向操作符 ">file" ,它重定向 1(stdout) 到 file 檔案,示意圖如下:
看到了什麼,1(stdout) 重定向到了 file 檔案,但 2(stderr) 仍指向終端,所有寫到 stderr 的都顯示到螢幕。所以,在使用重定向時,要非常非常小心給出它的操作循序!
另外在 Bash 中要注意:
$ command &>file
操作含義,與下面命令形式完全相同:
$ command >&file
但推薦第一種形式寫法。
4. 捨棄命令的螢幕輸出
$ command > /dev/null
特殊檔案 /dev/null 拋棄所有寫入它的內容(譯註:可認為 /dev/null 是一個大小恆為0的檔案,向它寫入任何內容,它都會將它清空,保持檔案大小為 0 。把它拷貝覆蓋已有檔案的動作也是把檔案內容清空的方法)。下面是標準檔案描述符示意圖:
類比地,組合之前學過的命令,我們可以捨棄命令的輸出和錯誤輸出,命令如下:
$ command >/dev/null 2>&1
或簡寫成:
$ command &>/dev/null
相應的標準輸入輸出描述符示意圖如下:
5. 重定向檔案內容作為命令的輸入
$ command <file
這裡, Bash 在執行命令前,試圖開啟 file 檔案並可讀。若開啟檔案失敗,命令失敗退出。若開啟讀取檔案成功,將使用開啟的檔案描述符作為命令的標準輸入描述符(stdin 號 0)。
上述動作完成後的標準輸入輸出描述符表示意圖如下:
下面是個例子,假定你想從 file 中讀取其第1行賦值給變數,命令如下:
$ read -r line < file
Bash 的內建命令 read
從標準輸入讀取單行資料。 使用輸入重定向操作符 < 使它從 file 檔案中讀取第1行資料。
6. 重定向多行文字到命令的輸入
$ command <<EOL
>你
>多行
>文字
>輸入
>到這兒
>EOL不是唯一文字
> EOL
>EOL
這裡,我們使用 即入文件(here-document) 重定向操作符 "<<MARKER" 的功能。這個操作指示 bash 從標準輸入(stdin 0)讀取多行輸入,直到某行(最後一行)只包含 MARKER 且無前導空白即退出輸入。最後一行的終止輸入標誌不會附加到輸入中。將最終的多行輸入給命令。
下面是個一般的例子。假設你拷貝了一堆的網上的網址(URLs)在系統剪貼簿裡,你想去掉所有的字首 http:// ,快速的單行命令如下:
$ sed 's|http://||' <<EOL
http://url1.com
http://url2.com
http://url3.com
EOL
這裡使用即入文件重定向符,將多行網址記錄輸入給 sed
命令,"sed" 命令使用正規表示式將所有行記錄中的 http:// 刪除。
以上例子輸出:
url1.com
url2.com
url3.com
7. 重定向單行字串文字到命令輸入
$ command <<< "foo bar baz"
舉個例子,假如你想系統剪貼簿裡的文字當作某個命令的輸入(文字是命令的引數或選項),可能的辦法是:
$ echo "粘帖剪貼簿內容" | command
現在你可以使用下面命令:
$ command <<< "粘帖剪貼簿內容"
當我學會這個技巧,它改變了我的人生!
8. 重定向所有的錯誤輸出到一個檔案
$ exec 2>file
$ command1
$ command2
$ ...
上述第一個命令列使用了 Bash 內建命令 exec
的功能,如果使用它重定向了標準輸入輸出(演示中是 2 號標準輸出 stderr ),它在本 shell 中永久有效!除非你再次修改它或退出這個 shell 。
例子中,使用 exec 2>file
命令將 2 號標準輸出(stderr)重定向到 file 檔案。之後,在這個 shell 環境裡所有命令的標準錯誤輸出都重定向到 file 檔案中。這是非常有用的一個技巧。當你想把所有命令或指令碼中的執行記錄到一個日誌檔案裡,而又不想在每條命令中都繁瑣地輸入重定向操作。
概括地講,exec
命令的主要功能是不建立子程式地呼叫一個 bash 命令或指令碼並執行,若命令列中指定了命令或指令碼,當前 shell 將被替換。我們演示的命令,在 exec
後並未給出任何命令,這僅僅利用了 exec
的執行重定向的功能,並無 shell 被替換。(譯註:個人覺得 exec 是個較分裂的命令。推薦參考這個 連結 中對 exec 命令的解釋。或直接看這個 AskUbuntu 中的回答)
9. 建立使用者的檔案描述符做為自定義的輸入描述符
$ exec 3<file
這裡,我們再次使用 exec
命令進行重定向的設定,指定 3<file
的意思是開啟 file 檔案可讀取,並將其分配給當前 shell 的 3 號輸入檔案描述符。當前 shell 的輸入輸出檔案描述符示意圖如下:
現在就可以從3號輸入檔案描述符讀取,如下命令:
$ read -u 3 line
上面的命令從 3 號輸入檔案描述符(重定向到 file 檔案)中讀取。
或用常規命令,諸如 grep
操作3號輸入檔案描述符:
$ grep "foo" <&3
這裡,3號輸入檔案描述符通過重定向代替了 grep 命令的預設 stdin 輸入。一定要記住,資源有限,故使用完使用者自開啟的檔案描述符後,要及時關閉它,釋放描述符號。可以再次使用。
使用完3號檔案描述符後,可以關閉它,使用如下命令:
$ exec 3>&-
這裡似乎3號檔案描述符被重定向給 &- 實際上,&- 就是 關閉這個檔案描述符 的一種語法。
10. 建立使用者的檔案描述符做為自定義的輸出描述符
$ exec 4>file
這裡,簡單地指示 bash 開啟可寫 file 檔案,並將其檔案描述符重定向成為4號輸出描述符。當前 shell 的輸入輸出檔案描述符示意圖如下:
正如你在上圖所見,輸入輸出檔案描述符並未按順序來,使用者可自由指定 0 只 255 之間的數字做為自定義開啟的檔案描述符號。
現在,簡單往4號輸出檔案描述符中寫入:
$ echo "foo" >&4
同樣,可以關閉4號輸出檔案描述符:
$ exec 4>&-
學會使用自定義輸入輸出檔案描述符,一切都是那麼簡單!
11. 開啟檔案用於讀寫
$ exec 3<>file
這裡使用 bash 的菱形操作符 <>
,菱形操作符開啟 file 檔案用於讀寫。
所以,可以執行如下例子:
$ echo "foo bar" > file # 將字串 "foo bar" 寫入 file 檔案。
$ exec 5<> file # 以讀寫方式開啟 file 檔案並重定向到5號描述符
$ read -n 3 var <&5 # 從5號輸入輸出描述符中讀取前3個字元。
$ echo $var
上面最後命令將輸出 "foo" ,只讀出前3個字元。
也可寫一些內容到 file 檔案:
$ echo -n + >&5 # 在檔案中第4字元位置寫入 "+"
$ exec 5>&- # 關閉5號檔案描述符
$ cat file
上面會輸出 foo+bar 。(譯註:實踐發現以永久重定向檔案描述符讀寫檔案,會保持讀寫位置)
12. 多個命令輸出重定向到檔案
$ (command1; command2) >file
上面例示使用 (commands)
執行命令操作,其中 bash 會將 ()
中的命令在一個建立的子程式中執行。
所以這裡的 command1
和 command2
執行在當前 shell 的子程式,同時,bash 將他們的輸出重定向到 file 檔案。
13. 通過檔案 shell 之間傳遞命令執行
開啟2個 shell 模擬終端,在第1個 shell 中,輸入命令:
$ mkfifo fifo
$ exec < fifo
在第2個 shell 中,輸入命令:
$ exec 3> fifo; echo 'echo test' >&3
現在看第1個 shell ,發現 echo test
命令執行輸出在第1個 shell 裡。你可以在第2個 shell 中傳送命令字串給3號描述符的方式給第1個 shell 傳送命令。
下面講解它是如何做到的。
在 shell 1 中,使用 mkfifo
建立 命名管道 -- fifo 。一個命名管道(也稱為 FIFO)性質與一般的管道相同,除了它是通過系統檔案系統訪問之外(建立命名管道將會在當前目錄下建立一個以命名管道名命名的檔案,這個檔案第一個屬性為 p 表示它為一個 管道(pipe),並且檔案大小始終顯示為 0)。它可供多程式讀寫(可供程式間通訊)。當程式間通過 FIFO --命名管道--交換資料時,系統核心並不會把資料寫入檔案儲存系統。因此,命名管道檔案大小永遠為 0 。僅僅把檔案系統中的檔名做為程式引用它的一個名稱罷了。
下一命令 exec < fifo
重定向 shell 1 的輸入為 fifo 。
接著,shell 2 重定向這個命名管道做為使用者輸出檔案描述符,並分配號為 3 。然後給 3 號輸出傳送字串 echo test
,這將傳給 fifo 檔案。
因此,shell 1 連線到 fifo 的標準輸入進入 shell 1 命令列並執行!很容易吧!
(譯註:原理很簡單,自己開兩個shell 去實踐練習一下。別的不多說,只提醒一點:當 shell 1 把自己的標準輸入重定向成命名管道之後,它就不能接受它自己終端的輸入了。如何恢復它回到原始的輸入?2個辦法,1.是重定向前備份。2.是使用它原始的名稱恢復,提示:輸入僅可用 /dev/tty 。至於怎樣讓它執行命令?自己想想,你棒棒噠!)
14. 通過 bash 訪問網站
$ exec 3<>/dev/tcp/www.bing.com/80
$ echo -e "GET / HTTP/1.1\n\n" >&3
$ cat <&3
Bash 處理 /dev/tcp/host/port 作為特殊檔案。它不需要在你的檔案系統中存在。這個只為 Bash 通過它開啟指定主機的網路介面。
以上例子,首先開啟可讀寫3號輸入輸出檔案描述符指向 /dev/tcp/www.bing.com/80 ,它將連結到 www.bing.com 網站的埠 80 。
下一步,向3號檔案描述符寫入 'GET / HTTP/1.1\n\n' (傳送HTTP請求),然後使用 cat
命令簡單讀取3號檔案描述符的內容。
類似地,你可以通過 /dev/udp/host/port 建立特殊檔案用於 UDP 連線。
使用 /dev/tcp/host/port 的方法,你甚至可以在 Bash 中寫出埠掃描的命令或指令碼!
(譯註:原文示例使用的是谷歌,我這裡換成了必應。實驗了一下,可能由於現在網站都使用 SSL 的緣故,第一次會獲得一個包含錯誤提示的 HTTP 響應內容,連線就關閉了)
15. 當重定向輸出時阻止寫入已有檔案
$ set -o noclobber
上面的命令開啟當前 shell 的 noclobber 選項。這個選項阻止當前 shell 使用重定向 '>' 操作覆蓋寫入已有的檔案。
如果你輸出重定向的檔案是一個已有檔案,將會得到一個錯誤提示:
$ > exFile
$ echo test > exFile
bash: exFile: cannot overwrite existing file
#bash: exFile: 不能覆寫已有檔案
如果你完全確定就是要覆蓋寫入已有檔案,可以使用 >| 重定向符:
$ echo test >| exFile
$ cat exFile
test
這個操作符成功超越 noclobber 選項。
16. 將輸入重定向到一個檔案和輸出到標準輸出
$ command | tee file
這個 tee
命令超級方便,雖然它不是 bash 的內建命令但很常用。它能把收到的輸入流輸出到標準輸出和一個檔案中。
如上例子,它將 command
命令的輸出分別輸出到 shell 螢幕和一個檔案。
下面是它工作原理示意圖:
17. 將一個處理程式(命令)的輸出重定向到另一個處理程式(命令)的輸入
$ command1 | command2
這是簡單的管道。我確定每個人都熟悉它。我放它到這裡只是為了教程的完整。僅僅提醒你,管道 的本質就是將命令 command1
的 輸出 重定向到 command2
命令的 輸入 。
可以用圖示意如下:
上圖可見,所有到達 command1 標準輸出(1 stdout)的內容都被重定向到了 command2 的標準輸入(0 stdin)。
更進一步請參考手冊命令 man 2 pipe
.
18. 傳送一個命令的標準輸出和標準錯誤輸出到另一命令程式
$ command1 |& command2
這個操作符在 bash 4.0 版本後出現。 |&
重定向操作符通過管道將 command1
命令的標準輸出和標準錯誤輸出都發到 command2
命令的標準輸入。
最新的 bash 4.0 版 的新功能未廣泛普及前,舊的,方便的方法是:
$ command1 2>&1 | command2
下面是標準輸入/輸出描述符 的變化示意圖:
前面的操作先將 command1 的 stderr(2) 重定向到 stdout(1) ,然後通過管道將標準輸出重定向到 command2 的標準輸入 stdin(0) 。
19. 為建立的檔案描述符賦名
$ exec {filew}>output_file
#譯註:引用命名檔案描述符,使用 &$ 如下命令:
$ echo test >&$filew #譯者加的命令
命名檔案描述符 是 bash 4.1 版後的功能特性。以 {varname}>output_file
定義了名為 varname 的輸出檔案描述符到指定檔案。你可以通過其名稱、檔案描述符號正常使用它。Bash 在內部會給它分配一個空閒的檔案描述符號。(譯註:這個命令很容易和臨時重定向到一個檔案混淆!關於如何用其名稱引用它,我寫到上面的程式碼註釋裡。如何找到其檔案描述符號,參見譯註1.)
20. 重定向操作的順序
你可以把重定向操作放到命令列的任何位置。看看下面3個例子,它們效果一樣:
$ echo hello >/tmp/example
$ echo >/tmp/example hello
$ >/tmp/example echo hello
喜歡上了 bash !
21. 交換標準 stdout 和 stderr
$ command 3>&1 1>&2 2>&3
這裡,首先將 stdout(&1) 複製一個副本 &3 --3號檔案描述符-- ,再使 stdout(&1) 成為 stderr(&2) -- 2號檔案描述符-- 的副本,最後使 stderr(&2) 成為 &3 的副本。這樣將 stdout 和 stderr 進行了交換。
讓我們用圖示展示每步過程。下面是命令開始時的檔案描述符狀態圖示:
然後,3>&1
重定向操作符指示,建立 3號輸出檔案描述符指向跟 &1 相同:
下一個1>&2
重定向操作符指示,將 1號標準輸出檔案描述符指向跟 &2 相同:
下一個2>&3
重定向操作符指示,將 2號標準輸出檔案描述符指向跟 &3 相同(即原始的 &1):
如果想關閉不再有用的好人 &3 --3號檔案描述符-- ,見如下命令:
$ command 3>&1 1>&2 2>&3 3>&-
之後的檔案描述符表示意如下:
如你所見,檔案描述符 1號 和 2號 已經交換。
22. 重定向 stdout 到一個程式,stderr 到另一程式
$ command > >(stdout_cmd) 2> >(stderr_cmd)
這行命令使用了重定向命令替換展開操作符 >()
。它將會執行 () 中的命令。而其標準輸入通過匿名管道連線到了 command
的標準輸出。接著下一個 >()
操作符將其標準輸入連線到了 command
的標準錯誤輸出。
對於上面的例子,第一個命令替換 >(stdout_cmd)
可能使 bash 返回 /dev/fd/60
檔案描述符。同時,第二個命令替換 >(stderr_cmd)
可能使 bash 返回 /dev/fd/61
檔案描述符。這2個檔案描述符由 bash 實時建立為等待讀取的命名管道。它們都等待某些命令程式往裡寫,以便它們讀取。
所以,上述命令展開後可能是這樣:
$ command > /dev/fd/60 2> /dev/fd/61
現在可看得清楚些, command 的 stdout 重定向到 /dev/fd/60 , stderr 重定向到 /dev/fd/61 。
當 command 輸出,內部程式 'stdout_cmd' 的命令執行將接受。當 command 的錯誤輸出,內部程式 'stderr_cmd' 的命令執行將接受。
23. 找出每個管道命令的退出碼
讓我們看一下幾個命令用管道串起來的例子,如下命令:
$ cmd1 | cmd2 | cmd3 | cmd4
你想找出這裡每個命令的退出狀態碼,該如何做?有沒有一個簡便的方法獲取每個命令的退出狀態碼,而不是 bash 簡單給出的最後一條命令的退出狀態碼。
Bash 的開發者想到了這點,他們加入了一個名為 'PIPESTATUS' 的陣列變數來保留管道命令流中每個命令的執行退出狀態碼。
陣列變數 PIPESTATUS 中的每個數字都對應於相應位置命令的退出狀態碼。下面是個例子:
$ echo 'men are cool' | grep 'moo' | sed 's/o/x/' | awk '{ print $1 }'
$ echo ${PIPESTATUS[@]}
0 1 0 0
上面例子中,命令 grep 'moo'
失敗,因此陣列變數 PIPESTATUS 中的第2個數為 1 。
建議
建議研究 bash 高手們的百科 演示重定向教程 和 bash 版本進化.
歡迎指正
享受文中所說的方法和技巧使用的樂趣吧,並且讓我知道您的想法。也許我漏掉了什麼,非常樂意收到您的指正。
----------------------------------------
----------------------------------------
譯註
1. 如何發現當前 bash 的所有檔案描述符
以下內容,譯者受網文 Linux: Find All File Descriptors Used By a Process 啟發,針對在 bash 中發現其所有檔案描述符的任務進行創作。
一、找到當前 bash 程式 ID -- PID
方法1:使用檢視程式命令,如 ps 命令
語法:
ps aux | grep [程式名稱]
ps 命令,參考本站wiki ps 命令(檢視程式) 。
對於我們的情況,程式名稱 是 bash ,命令如下:
$ ps aux | grep bash
可能的輸出如下:
aman 7146 0.0 0.2 30748 5728 pts/4 Ss 09:42 0:00 bash
aman 7978 0.0 0.2 24432 5516 pts/18 Ss+ 13:56 0:00 bash
aman 8022 0.0 0.0 15964 928 pts/4 S+ 14:10 0:00 grep --color=auto bash
這裡,我專門開啟了2個 shell ,哪個是我們當前的 shell 呢?方法很多,使用 tty
命令找出當前的 tty 與上面的輸出對比。找到本 shell 的 PID 是 7146 。
方法2:使用 pidof 命令
語法:
pidof [程式名稱]
這個方法不適用同時開了多個 shell 的例子,但只有一個 shell 時會很簡單,如下命令:
$ pidof bash
7146
二、根據 bash 的 PID 找到其使用的所有檔案描述符
方法1:檢視 /proc/pid/fd 目錄
如下所示命令及輸出:
$ ls -l /proc/7146/fd
total 0
lrwx------ 1 aman aman 64 12月 27 09:42 0 -> /dev/pts/4
l-wx------ 1 aman aman 64 12月 27 09:42 1 -> /dev/pts/4
l-wx------ 1 aman aman 64 12月 27 09:42 10 -> /home/aman/test/nFD
lrwx------ 1 aman aman 64 12月 27 09:42 2 -> /dev/pts/4
lrwx------ 1 aman aman 64 12月 27 12:02 255 -> /dev/pts/4
我之前在這個 shell 下,使用 $ exec {FD1}> nFD
命令建立了一個永久命名檔案描述符重定向到了當前目錄下的 nFD 檔案。在這裡我們看見 bash 內部給它分配了檔案描述符號 10 。這樣,你在重定向輸出時就即可使用 &$FD1 或 &10 都會輸出到 nFD 檔案。
我的執行示意如下:
$ echo 'line 1' >&10
$ echo 'line 2' >&$FD1 #這行命令與上行輸出到相同目的地
$ cat nFD
line 1
line 2
方法2:使用 lsof 命令
語法:
lsof -a -p {輸入PID}
lsof 命令,參考 ’ lsof command ' 。我的命令示意如下:
$ lsof -a -p 7146
輸出:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 7146 aman cwd DIR 8,5 4096 178864 /home/aman/test
bash 7146 aman rtd DIR 8,5 4096 2 /
bash 7146 aman txt REG 8,5 1037528 134435 /bin/bash
bash 7146 aman mem REG 8,5 101200 265464 /lib/x86_64-linux-gnu/libresolv-2.23.so
...
...
...
bash 7146 aman 0u CHR 136,4 0t0 7 /dev/pts/4
bash 7146 aman 1w CHR 136,4 0t0 7 /dev/pts/4
bash 7146 aman 2u CHR 136,4 0t0 7 /dev/pts/4
bash 7146 aman 10w REG 8,5 16 178735 /home/aman/test/nFD
bash 7146 aman 255u CHR 136,4 0t0 7 /dev/pts/4
見上面輸出倒數第二行,可知 nFD 檔案的重定向檔案描述符號為 10 。