Unix shell輸入輸出重定向

寫程序的時候,適當地打印出一些進度或者日誌信息常常能幫助我們跟蹤程序的運行結果。但是,這些結果或者日誌信息打印到屏幕上並不能作爲以後檢查問題的依據。這就是重定向的作用,寫程序的時候,我們可以方便的將相關的信息打印到屏幕或者是從鍵盤接收輸入(這樣的好處就是避免直接操作文件),利用重定向我們可以很方便地將輸入輸出重定向到文件或者其它地方。

1、文件描述符(下面部分來自維基百科)

文件描述符(File descriptor)是計算機科學中的一個術語,是一個用於表述指向文件的引用的抽象化概念。

文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核爲每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞着文件描述符展開。但是文件描述符這一概念往往只適用於UNIX、Linux這樣的操作系統。

文件描述符的優點主要有兩個:基於文件描述符的I/O操作兼容POSIX標準;在UNIX、Linux的系統調用中,大量的系統調用都是依賴於文件描述符。文件描述符的概念存在兩大缺點:在非UNIX/Linux操作系統上(如Windows NT),無法基於這一概念進行編程;由於文件描述符在形式上不過是個整數,當代碼量增大時,會使編程者難以分清哪些整數意味着數據,那些意味着文件描述符,因此,完成的代碼可讀性也就會變得很差,這一點一般通過消除魔術數字來解決。

對於ANSI C規範中定義的標準庫的文件I/O操作。ANSI C規範給出了一個解決方法,就是使用FILE結構體的指針。事實上,UNIX/Linux平臺上的FILE結構體的實現中往往都是封裝了文件描述符變量在其中。

2、輸入輸出重定向

2.1 stdin、stdout和stderr

簡單地說,文件描述符是與已打開文件或設備相關聯的整數,它們保持和已打開文件或設備的關聯。最爲常見的文件描述符是stdin、stdout和stderr,它們分別是0、1、2,是系統保留的文件描述符,分別對應標準輸入、標準輸出和標準錯誤。Unix系統會默認打開這三個文件描述符,並將stdin關聯到鍵盤,將stdout和stderr關聯到屏幕。這裏將的輸入輸出重定向主要就是將這三個文件描述符重新定向到其它我們希望的文件或者設備。對於stdin,stdout和stderr重定向一般採用的操作符主要有<、>和>>,在沒有指定的具體文件操作符的情況下,缺省是這樣的:command < file.txt相當於command 0< file.txt,也就是說默認是將文件重定向到文件描述符0;command > file.txt相當於command 1> file.txt也就是說默認將文件描述符1重定向到文件,>>和>相同。如果要重定向stderr,就要顯示指定,比如command 2> file.txt,將command的錯誤信息輸出到file.txt。

2.2 重定向輸入

爲了避免每次手動輸入數據,我們可以將數據寫入一個文件,然後使用重定向將輸入重定向到該文件。這裏爲了演示用的是linux的cat命令,cat命令如果不加參數,會讀取標準輸入並將其輸出到屏幕(實際上,cat用‘-’做參數也是這個效果,cat -),效果如下:

[lfqy@localhost ~]$ cat 
Hello, world!
Hello, world!
Ni hao!
Ni hao!
I'm unhappy.
I'm unhappy.//這裏按下ctrl+D相當於EOF
[lfqy@localhost ~]$ 
下面將上面的內容寫入文件test.txt,然後將cat的輸入重定向到該文件,這裏用到的是"<",這個操作符:

[lfqy@localhost ~]$ cat <test.txt 
Hello, world!
Ni hao!
I'm unhappy.
[lfqy@localhost ~]$ 
實際上,像上面說的cat 0<test.txt效果也一樣。當然,這裏只是用cat做一個例子,想用cat查看一個文件的內容何須這麼麻煩。

2.2 重定向輸出

2.2.1 重定向標準輸入和標準錯誤

爲了測試標準錯誤,首先新建了一個test目錄,然後在裏面創建了三個文件f1.txt、f2.txt和f.txt。在f.txt中寫入上面的幾句話,然後將f1.txt和f2.txt的權限設置爲000(chmod 000 f1.txt)。下面開始分別將標準輸出和標準錯誤重定向。

1、沒有重定向之前
[lfqy@localhost test]$ cat f*
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
Hello, world!
Ni hao!
I'm unhappy.
2、重定向標準輸出
[lfqy@localhost test]$ cat f* > stdout.txt
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
[lfqy@localhost test]$ cat stdout.txt 
Hello, world!
Ni hao!
I'm unhappy.
3、重定向標準輸出和標準錯誤
[lfqy@localhost test]$ cat f* 2> stderr.txt
Hello, world!
Ni hao!
I'm unhappy.
[lfqy@localhost test]$ cat stderr.txt 
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
4、同時分別將標準輸出重定向到stdout.txt,將標準錯誤重定向到stderr.txt
[lfqy@localhost test]$ rm stderr.txt stdout.txt 
[lfqy@localhost test]$ cat f* 2> stderr.txt >stdout.txt
[lfqy@localhost test]$ cat stderr.txt stdout.txt 
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
Hello, world!
Ni hao!
I'm unhappy.

如果輸出重定向的文件不存在,該文件會默認被創建;如果文件存在並有內容,其中原來的內容都會被清空。當然,如果想要新的重定向的內容追加在原來內容的後面,可將上面的'>'換成'>>',可以達到效果。

2.2.2 將標準輸出和標準錯誤同時重定向到一個文件

這個實現起來有好幾種辦法,方法如下:

1、cat f* &> stdall.txt
[lfqy@localhost test]$ cat f* &> stdall.txt
[lfqy@localhost test]$ cat stdall.txt 
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
Hello, world!
Ni hao!
I'm unhappy.
2、cat f* 1> stdall0.txt 2>&1
[lfqy@localhost test]$ cat f* 1> stdall0.txt 2>&1
[lfqy@localhost test]$ cat stdall0.txt 
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
Hello, world!
Ni hao!
I'm unhappy.
反例:
[lfqy@localhost test]$ cat f* 2>&1 1> stdall0.txt
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
注意上面的反例,不能是cat f* 2>&1 1> stdall0.txt(因爲如果先將標準錯誤重定向到標準輸出,而這時標準輸出沒有被重定向,仍然是輸出到屏幕,所以會導致將標準錯誤重定向到屏幕。),而必須是cat f* 1> stdall0.txt 2>&1(先將標準輸出重定向到文件,這時候,所有到標準輸出的內容都會被重定向到文件,因此後面重定向到標準輸出的標準錯誤也會輸出到文件。)。

上面1用的是一個稍微特殊的操作符&>可以同時重定向標準輸出和標準錯誤;2是將標準錯誤重定向到標準輸出,然後重定向標準輸出來達到目的。二者的實現思路稍有不同。這裏之所以要用cat f* 1> stdall0.txt 2>&1,之所以要在1前面加一個&,是因爲1作爲一個系統默認創建的文件描述符,要用&來引用它,下面應該也會講到。

2.2.3 新技能get

(1) 丟棄不想要的輸出

有一個類似於垃圾桶的設備文件:/dev/null。將不想要的輸出重定向到該文件就可以了。

1、丟棄標準輸出
[lfqy@localhost test]$ cat f* > /dev/null
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
2、丟棄標準錯誤
[lfqy@localhost test]$ cat f* 2> /dev/null
Hello, world!
Ni hao!
I'm unhappy.
3、都丟棄(下面三種效果相同)
[lfqy@localhost test]$ cat f* 2> /dev/null 1>/dev/null
[lfqy@localhost test]$ cat f* &> /dev/null 
[lfqy@localhost test]$ cat f* 1>/dev/null 2>&1
(2)既保存輸出到文件,又讓輸出顯示在屏幕

有時候,我們要將程序的輸出存檔,而又想看到程序的輸出(上面的重定向沒辦法看到輸出,只能後來查看文件)。這裏,用到一個命令tee,它能夠讀取標準輸入的內容,然後將其既輸出到標準輸出又寫入到文件(tee - read from standard input and write to standard output and files)。這裏要注意的是,這裏需要將命令的輸出送到管道,然後由tee來接收管道的輸入,而只有標準輸出的內容能夠經過管道,所以先要將標準錯誤重定向到標準輸出。

[lfqy@localhost test]$ cat f* 2>&1 | tee tee.txt
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
Hello, world!
Ni hao!
I'm unhappy.
[lfqy@localhost test]$ cat tee.txt 
cat: f1.txt: Permission denied
cat: f2.txt: Permission denied
Hello, world!
Ni hao!
I'm unhappy.
這裏會自動創建不存在的文件,並清空已存在文件原來的內容,如果想用追加模式,請使用cat f* 2>&1 | tee -a tee.txt。
3、創建自定義的文件描述符

0、1和2是系統保留的文件描述符,我們也可以自己創建自定義的文件描述符。自定義的文件描述符主要有三種模式:只讀模式,截斷模式和追加模式。假定已經創建了文件描述符num,可以使用&num來引用它,上文中用過&1就是這個道理。

3.1 只讀模式

使用"exec num<filename"創建只讀模式的文件描述符;只讀方式的文件描述符只能讀取一次,如需二次讀取,需要對文件描述符重新創建。

[lfqy@localhost test]$ exec 5<f.txt
[lfqy@localhost test]$ cat <&5
Hello, world!
Ni hao!
I'm unhappy.
[lfqy@localhost test]$ cat <&5//二次讀取的時候沒有任何輸出
3.2 截斷模式

截斷模式就是指在創建該文件描述符的時候,對應文件中原來的內容將會被清空。使用"exec num>filename"的模式創建截斷模式截斷模式的文件描述符創建之後,所有輸出到該文件描述符的內容都會依次追加在對應文件的後面,只是該文件描述符創建之前的內容被清空了。

[lfqy@localhost test]$ cat f.txt 
Hello, world!
Ni hao!
I'm unhappy.
[lfqy@localhost test]$ exec 7>f.txt
[lfqy@localhost test]$ cat f.txt 
[lfqy@localhost test]$ echo "Love" >&7
[lfqy@localhost test]$ echo "Happy" >&7
[lfqy@localhost test]$ cat f.txt 
Love
Happy
3.3 追加模式

使用"exec num>>filename"創建追加模式的文件描述符,和截斷模式不同,在創建該文件描述符的時候,對應文件中原來的內容不會被清空,所有輸出到該文件描述符的內容都會依次追加在對應文件的後面。

[lfqy@localhost test]$ cat f.txt 
Love
Happy
[lfqy@localhost test]$ exec 8>>f.txt
[lfqy@localhost test]$ cat f.txt 
Love
Happy
[lfqy@localhost test]$ echo "Friendship" >&7
[lfqy@localhost test]$ echo "Flair" >&7
[lfqy@localhost test]$ cat f.txt 
Love
Happy
Friendship
Flair

好吧,關於文件描述符今天就到這裏了,後面學到新的內容會再補上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章