本文目錄: 1.1 文件描述符(file description,fd) 1.2 文件描述符的複製 1.3 重定向順序很重要:">file 2>&1"和"2>&1 >file" 1.4 改變當前shell環境的重定向目標 1.5 關閉文件描述符 1.6 打開文件 1.7 文件描述符的移動 1.8 經 ...
本文目錄:
1.1 文件描述符(file description,fd)
1.2 文件描述符的複製
1.3 重定向順序很重要:">file 2>&1"和"2>&1 >file"
1.4 改變當前shell環境的重定向目標
1.5 關閉文件描述符
1.6 打開文件
1.7 文件描述符的移動
1.8 經典示例
基本的重定向功能想必都理解。本文對shell環境下的IO重定向稍作深入,相信看完後,能夠徹底理解 >file 2>&1 。
1.1 文件描述符(file description,fd)
文件描述符是IO重定向中的重要概念。文件描述符使用數字表示,它指明瞭數據的流向特征。
軟體設計認為,程式應該有一個數據來源、數據出口和報告錯誤的地方。在Linux系統中,它們分別使用描述符0、1、2來表示,這3個描述符預設的目標文件(設備)分別是/dev/stdin、/dev/stdout、/dev/stderr,它們分別是各個終端字元設備的軟鏈接。
[root@mariadb ~]# ll /dev/std* lrwxrwxrwx 1 root root 15 Apr 2 07:57 /dev/stderr -> /proc/self/fd/2 lrwxrwxrwx 1 root root 15 Apr 2 07:57 /dev/stdin -> /proc/self/fd/0 lrwxrwxrwx 1 root root 15 Apr 2 07:57 /dev/stdout -> /proc/self/fd/1 [root@mariadb ~]# ll /proc/self/fd/ total 0 lrwx------ 1 root root 64 Apr 6 03:53 0 -> /dev/pts/2 lrwx------ 1 root root 64 Apr 6 03:53 1 -> /dev/pts/2 lrwx------ 1 root root 64 Apr 6 03:53 2 -> /dev/pts/2 lr-x------ 1 root root 64 Apr 6 03:53 3 -> /proc/14038/fd
在Linux中,每一個進程打開時都會自動獲取3個文件描述符0、1和2,分別表示標準輸入、標準輸出、和標準錯誤,如果要打開其他文件,則文件描述符必須從3開始標識。對於我們人為要打開的描述符,建議使用9以內的描述符,超過9的描述符可能已經被系統內部分配給其他進程。
文件描述符說白了就是系統為了跟蹤這個打開的文件而分配給它的一個數字,這個數字和文件綁定在一起,數據流入描述符的時候也表示流入文件。
而Linux中萬物皆文件,這些文件都可以分配描述符,包括套接字。
程式在打開文件描述符的時候,有三種可能的行為:從描述符中讀、向描述符中寫、可讀也可寫。從lsof的FD列可以看出程式打開這個文件是為了從中讀數據,還是向其中寫數據,亦或是既讀又寫。例如,tail命令監控文件時,就是打開文件從中讀數據的(3r的r是read,w是write,u是read and write)。
[root@mariadb ~]# lsof -n | grep "/a.sh" | column -t tail 13563 root 3r REG 8,2 182 69632966 /root/a.sh
1.2 文件描述符的複製(duplicate)
文件描述符的複製表示覆制文件描述符到另一個文件描述符中以作副本文件。使用"&"進行複製。如果不好理解複製的意思,將其理解為"綁定"、"重用"。
[n]<&word :將文件描述符n綁定到word 代表的文件或描述符。可以理解為文件描述符n重用word代表的文件或描述符。n不指定則預設為0(標準輸入就是0),表示標準輸入也將輸入到word所代表的文件或描述符中。
[n]>&word :將文件描述符n綁定到word 代表的文件或描述符。可以理解為文件描述符n重用word代表的文件或描述符。n不指定則預設為1(標準輸出就是1),表示標準輸出也將輸出到word所代表的文件或描述符中。
註意,每綁定一次,都會立刻重定向到對應的文件。請結合下麵的例子感受。
例如, 3>&1 表示fd=3綁定到fd=1上,而fd=1目前的重定向目標文件是/dev/stdout,因此fd=3也重定向到/dev/stdout,以後進程將數據寫入fd=3的時候,將直接輸出到屏幕。如果用"複製"來理解,就是fd=3是當前fd=1的一個副本,即指向/dev/stdout設備。如果後面改變了fd=1的輸出目標(如file1),由於fd=3的目標仍然是/dev/stdout,所以可以拿fd=3來還原fd=1使其目標變回/dev/stdout。
再例如, cat <&1 表示fd=0綁定到fd=1上,而此時fd=1的重定向文件是/dev/stdout,因此此時/dev/stdout既是標準輸入設備,也是標準輸出設備,也就是說進程從/dev/stdout(屏幕)接受輸入,輸入後再直接輸出到/dev/stdout。以下是結果:
[root@mariadb ~]# cat <&1 q # 進入互動式,輸入數據 q # 直接輸出
1.3 重定向順序很重要:">file 2>&1"和"2>&1 >file"
想必很多人都知道 >file 2>&1 的作用,它等價於 &>file ,表示標準輸出和標準錯誤都重定向到file中。那它和 2>&1 >file 有什麼區別呢?
首先解釋 >file 2>&1 。這裡分兩個過程:先打開file,再將fd=1重定向到file文件上,這樣file文件就成了標準輸出的輸出目標;之後再將fd=2綁定(註意,是綁定不是重定向)到fd=1上,而fd=1此時已經重定向到file文件上,因此fd=2也重定向到file上。所以,最終的結果是標準輸出重定向到file上,標準錯誤也重定向到file上。
再解釋 2>&1 >file 。這裡也分兩個過程:先將fd=2綁定到fd=1上,而此時fd=1重定向的文件是預設的/dev/stdout,所以fd=2也重定向到/dev/stdout;之後再將fd=1重定向到file文件上(註意,不是綁定是重定向)。也就是說,這裡的標準錯誤和標準輸出仍然是分開輸出的,只不過是使用/dev/stdout替代了/dev/stderr,使用file替代了/dev/stdout。所以,最終的結果是標準錯誤輸出到/dev/stdout,即屏幕上,而標準輸出將輸出到file文件中。
可以使用下麵的命令來測試 2>&1 >file 。第一個ls命令是正確的,結果輸出到/tmp/a.log中,第二個ls命令是錯誤的,結果將直接輸出到屏幕上。
[root@mariadb ~]# ls /boot 2>&1 >/tmp/a.log [root@mariadb ~]# ls sjdfk 2>&1 >/tmp/a.log ls: cannot access sjdfk: No such file or directory
最後,也許你已經發現了,綁定和重定向是不同的,綁定不應該稱為重定向。區分這兩個概念,在實際應用的過程中能解決非常多的疑惑。在本文結尾的最後一個例子中,你將能非常明確地體會到綁定和重定向的區別。
1.4 改變當前shell環境的重定向目標
如果在命令中直接改變重定向的位置,那麼命令執行結束的時候描述符會自動還原。正如上面的 ls /boot 2>&1 >/tmp/a.log 命令,在ls執行結束後,fd=2還原回預設的/dev/stderr,fd=1還原回預設的/dev/stdout。
但是我們可以通過exec程式直接在當前的shell環境下改變重定向目標,只有在當前shell退出的時候才會釋放描述符的綁定。
例如:下麵的命令將標準錯誤fd=2重定向到fd=3對應的文件上。
exec 2>&3
因此,我們可能在一段程式執行結束後,需要將描述符還原到原來的位置,並關閉不再需要的描述符。畢竟描述符也是資源,是有限的(ulimit -n)。
1.5 關閉文件描述符
[n]>&-
[n]<&-
關閉文件描述符的方式是將 [n]>&word 和 [n]<&word 中的word使用符號"-",這表示釋放fd=n描述符,且關閉其指向的文件。
1.6 打開文件
[n]<> filename :打開filename,並指定其文件描述符為n,該描述符是可讀、可寫的描述符。若不指定n則預設為0,若filename文件不存在,則先創建filename文件。
例如:
[root@mariadb ~]# exec 3<> /tmp/a.log [root@mariadb ~]# lsof -n | grep "/a.log" | column -t bash 13637 root 3u REG 8,2 292018 69632965 /tmp/a.log
如果再 exec 1>&3 將fd=0綁定到fd=3上,那麼/tmp/a.log就成了標準輸入的來源。
1.7 文件描述符的移動
文件描述符的移動表示將文件描述符1移動到描述符2上,同時關閉文件描述符1。
[n]>&digit- :將文件描述符digit代表的輸出文件移動到n上,並關閉digit值的描述符。
[n]<&digit- :將文件描述符digit代表的輸入文件移動到n上,並關閉digit值的描述符。
例如:
[root@mariadb ~]# exec 3<> /tmp/a.log [root@mariadb ~]# lsof -n | grep "/a.log" | column -t bash 13637 root 3u REG 8,2 292018 69632965 /tmp/a.log [root@mariadb ~]# exec 1>&3- # 將3移動到1上,關閉3 [root@mariadb ~]# lsof -n | grep "/a.log" | column -t # 在另一個bash視窗查看 bash 13637 root 1u REG 8,2 292018 69632965 /tmp/a.log
可見,fd=3移動到fd=1後,原本與fd=3關聯的/tmp/a.log已經關聯到fd=1上。
1.8 經典示例
(1). 示例一:
以下是《Advanced Bash-Scripting Guide》中的示例:
echo 1234567890 > File # (1).寫字元串到"File". exec 3<> File # (2).打開"File"並且給它分配fd 3. read -n 4 <&3 # (3).只讀4 個字元. echo -n . >&3 # (4).寫一個小數點. exec 3>&- # (5).關閉fd 3. cat File # (6).1234.67890
(1)向文件File中寫入幾個字元。
(2)打開文件File以備read/write,並分配fd=3給該文件。
(3)將fd=0綁定到fd=3上,而fd=3的重定向目標為File,所以fd=0的目標也是File,即從File中讀取數據。這裡讀取4個字元,由於read命令中沒有指定變數,因此分配給預設變數REPLY。註意,這個命令執行結束後,fd=0的重定向目標會變回/dev/stdin。
(4)將fd=1綁定到fd=3上,而fd=3的重定向目標文件為File,所以fd=1的目標也是File,即數據寫入到File中。這裡寫入一個小數點。註意,這個命令結束後,fd=1的重定向目標回變回/dev/stdout。
(5)關閉fd=3,這也會關閉其指向的文件File。
(6)File文件中已經寫入了一個小數點。如果此時執行 echo $REPLY ,將輸出"1234"。
(2). 示例二:關於描述符恢復、關閉
exec 6>&1 # (1) exec > /tmp/file.txt # (2) echo "---------------" # (3) exec 1>&6 6>&- # (4) echo "===============" # (5)
(1)首先將fd=6綁定到fd=1,此時fd=1的重定向目標為/dev/stdout,因此fd=6的重定向目標為/dev/stdout。
(2)將fd=1重定向到/tmp/file.txt文件。此後所有標準輸出都將寫入到/tmp/file.txt中。
(3)寫入數據。該數據將寫入到/tmp/file.txt中。
(4)將fd=1綁定回fd=6,此時fd=6的重定向目標為/dev/stdout,因此fd=1將恢復到/dev/stdout上。最後將fd=6關閉。
(5)寫入數據,這段數據將輸出在屏幕上。
可能你會疑惑,為什麼要先將fd=1綁定到fd=6上,再用fd=6來恢復fd=1,恢復的時候直接將fd=1重定向回/dev/stdout不就可以了嗎?
實際上,這裡借用fd=6這個中轉描述符是為了方便操作。你可以不它,但是再恢復fd=1的重定向目標的時候,應該重定向到/dev/{偽終端字元設備}上,而不是/dev/stdout,因為/dev/stdout是軟鏈接,其目標指向/proc/self/fd/1,但該文件還是軟鏈接,它指向/dev/{偽終端字元設備}。同理/dev/stdin和/dev/stderr都一樣。
因此,如果你當前所在的終端如果是pts/2,那麼可以使用下麵的命令來實現上面同樣的功能:
exec > /tmp/file.txt echo "---------------" exec >/dev/pts/2 echo "==============="
如果不借用fd=6這個中轉描述符,你要先去獲取並記住當前shell所在的終端,很不方便。而且,如果要恢復的不是fd={0,1,2},那就更麻煩。
最後給張描述符複製、恢復的過程實例圖:
回到Linux系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
回到網站架構系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7576137.html
回到資料庫系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7586194.html
轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/8727401.html
註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!