Linux Shell重定向(輸入輸出重定向)精講

Linux Shell 重定向分爲兩種,一種輸入重定向,一種是輸出重定向;從字面上理解,輸入輸出重定向就是「改變輸入與輸出的方向」的意思。

那麼,什麼是輸入輸出方向呢?標準的輸入輸出方向又是什麼呢?

一般情況下,我們都是從鍵盤讀取用戶輸入的數據,然後再把數據拿到程序(C語言程序、Shell 腳本程序等)中使用;這就是標準的輸入方向,也就是從鍵盤到程序。

反過來說,程序中也會產生數據,這些數據一般都是直接呈現到顯示器上,這就是標準的輸出方向,也就是從程序到顯示器。


我們可以把觀點提煉一下,其實輸入輸出方向就是數據的流動方向:

  • 入方向就是數據從哪裏流向程序。數據默認從鍵盤流向程序,如果改變了它的方向,數據就從其它地方流入,這就是輸入重定向。
  • 輸出方向就是數據從程序流向哪裏。數據默認從程序流向顯示器,如果改變了它的方向,數據就流向其它地方,這就是輸出重定向。

硬件設備和文件描述符

計算機的硬件設備有很多,常見的輸入設備有鍵盤、鼠標、麥克風、手寫板等,輸出設備有顯示器、投影儀、打印機等。不過,在 Linux 中,標準輸入設備指的是鍵盤,標準輸出設備指的是顯示器。

Linux 中一切皆文件,包括標準輸入設備(鍵盤)和標準輸出設備(顯示器)在內的所有計算機硬件都是文件。

了表示和區分已經打開的文件,Linux 會給每個文件分配一個 ID,這個 ID 就是一個整數,被稱爲文件描述符(File Descriptor)。
 

表1:與輸入輸出有關的文件描述符
文件描述符 文件名 類型 硬件
0 stdin 標準輸入文件 鍵盤
1 stdout 標準輸出文件 顯示器
2 stderr 標準錯誤輸出文件 顯示器


Linux 程序在執行任何形式的 I/O 操作時,都是在讀取或者寫入一個文件描述符。一個文件描述符只是一個和打開的文件相關聯的整數,它的背後可能是一個硬盤上的普通文件、FIFO、管道、終端、鍵盤、顯示器,甚至是一個網絡連接。

stdin、stdout、stderr 默認都是打開的,在重定向的過程中,0、1、2 這三個文件描述符可以直接使用。

Linux Shell 輸出重定向

輸出重定向是指命令的結果不再輸出到顯示器上,而是輸出到其它地方,一般是文件中。這樣做的最大好處就是把命令的結果保存起來,當我們需要的時候可以隨時查詢。Bash 支持的輸出重定向符號如下表所示。
 

表2:Bash 支持的輸出重定向符號
類 型 符 號 作 用
標準輸出重定向 command >file 以覆蓋的方式,把 command 的正確輸出結果輸出到 file 文件中。
command >>file 以追加的方式,把 command 的正確輸出結果輸出到 file 文件中。
標準錯誤輸出重定向 command 2>file 以覆蓋的方式,把 command 的錯誤信息輸出到 file 文件中。
command 2>>file 以追加的方式,把 command 的錯誤信息輸出到 file 文件中。
正確輸出和錯誤信息同時保存 command >file 2>&1 以覆蓋的方式,把正確輸出和錯誤信息同時保存到同一個文件(file)中。
command >>file 2>&1 以追加的方式,把正確輸出和錯誤信息同時保存到同一個文件(file)中。
command >file1 2>file2 以覆蓋的方式,把正確的輸出結果輸出到 file1 文件中,把錯誤信息輸出到 file2 文件中。
command >>file1  2>>file2 以追加的方式,把正確的輸出結果輸出到 file1 文件中,把錯誤信息輸出到 file2 文件中。
command >file 2>file 不推薦】這兩種寫法會導致 file 被打開兩次,引起資源競爭,所以 stdout 和 stderr 會互相覆蓋,我們將在《結合Linux文件描述符談重定向,徹底理解重定向的本質》一節中深入剖析。
command >>file 2>>file


在輸出重定向中,>代表的是覆蓋,>>代表的是追加。

注意

輸出重定向的完整寫法其實是fd>file或者fd>>file,其中 fd 表示文件描述符,如果不寫,默認爲 1,也就是標準輸出文件。

當文件描述符爲 1 時,一般都省略不寫,如上表所示;當然,如果你願意,也可以將command >file寫作command 1>file,但這樣做是多此一舉。

當文件描述符爲大於 1 的值時,比如 2,就必須寫上。

需要重點說明的是,fd>之間不能有空格,否則 Shell 會解析失敗;>file之間的空格可有可無。爲了保持一致,我習慣在>兩邊都不加空格。

下面的語句是一個反面教材:

echo "c.biancheng.net" 1 >log.txt

注意1>之間的空格。echo 命令的輸出結果是c.biancheng.net,我們的初衷是將輸出結果重定向到 log.txt,但是當你打開 log.txt 文件後,發現文件的內容爲c.biancheng.net 1,這就是多餘的空格導致的解析錯誤。也就是說,Shell 將該條語句理解成了下面的形式:

echo "c.biancheng.net" 1 1>log.txt

輸出重定向舉例

【實例1】將 echo 命令的輸出結果以追加的方式寫入到 demo.txt 文件中。


 
  1. #!/bin/bash
  2.  
  3. for str in "C語言中文網" "http://c.biancheng.net/" "成立7年了" "日IP數萬"
  4. do
  5. echo $str >>demo.txt #將輸入結果以追加的方式重定向到文件
  6. done

運行以上腳本,使用cat demo.txt查看文件內容,顯示如下:
C語言中文網
http://c.biancheng.net/
成立7年了
日IP數萬

【實例2】將ls -l命令的輸出結果重定向到文件中。

[c.biancheng.net]$ ls -l  #先預覽一下輸出結果
總用量 16
drwxr-xr-x. 2 root     root      21 7月   1 2016 abc
-rw-r--r--. 1 mozhiyan mozhiyan 399 3月  11 17:12 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan  67 3月  22 17:16 demo.txt
-rw-rw-r--. 1 mozhiyan mozhiyan 278 3月  16 17:17 main.c
-rwxr-xr-x. 1 mozhiyan mozhiyan 187 3月  22 17:16 test.sh
[c.biancheng.net]$ ls -l >demo.txt  #重定向
[c.biancheng.net]$ cat demo.txt  #查看文件內容
總用量 12
drwxr-xr-x. 2 root     root      21 7月   1 2016 abc
-rw-r--r--. 1 mozhiyan mozhiyan 399 3月  11 17:12 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan   0 3月  22 17:21 demo.txt
-rw-rw-r--. 1 mozhiyan mozhiyan 278 3月  16 17:17 main.c
-rwxr-xr-x. 1 mozhiyan mozhiyan 187 3月  22 17:16 test.sh

錯誤輸出重定向舉例

命令正確執行是沒有錯誤信息的,我們必須刻意地讓命令執行出錯,如下所示:

[c.biancheng.net]$ ls java  #先預覽一下錯誤信息
ls: 無法訪問java: 沒有那個文件或目錄
[c.biancheng.net]$ ls java 2>err.log  #重定向
[c.biancheng.net]$ cat err.log  #查看文件
ls: 無法訪問java: 沒有那個文件或目錄

正確輸出和錯誤信息同時保存

【實例1】把正確結果和錯誤信息都保存到一個文件中,例如:

[c.biancheng.net]$ ls -l >out.log 2>&1
[c.biancheng.net]$ ls java >>out.log 2>&1
[c.biancheng.net]$ cat out.log
總用量 12
drwxr-xr-x. 2 root     root      21 7月   1 2016 abc
-rw-r--r--. 1 mozhiyan mozhiyan 399 3月  11 17:12 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan 278 3月  16 17:17 main.c
-rw-rw-r--. 1 mozhiyan mozhiyan   0 3月  22 17:39 out.log
-rwxr-xr-x. 1 mozhiyan mozhiyan 187 3月  22 17:16 test.sh
ls: 無法訪問java: 沒有那個文件或目錄

out.log 的最後一行是錯誤信息,其它行都是正確的輸出結果。

【實例2】上面的實例將正確結果和錯誤信息都寫入同一個文件中,這樣會導致視覺上的混亂,不利於以後的檢索,所以我建議把正確結果和錯誤信息分開保存到不同的文件中,也即寫成下面的形式:

ls -l >>out.log 2>>err.log

這樣一來,正確的輸出結果會寫入到 out.log,而錯誤的信息則會寫入到 err.log。

/dev/null 文件

如果你既不想把命令的輸出結果保存到文件,也不想把命令的輸出結果顯示到屏幕上,干擾命令的執行,那麼可以把命令的所有結果重定向到 /dev/null 文件中。如下所示:

ls -l &>/dev/null

大家可以把 /dev/null 當成 Linux 系統的垃圾箱,任何放入垃圾箱的數據都會被丟棄,不能恢復。

Linux Shell 輸入重定向

輸入重定向就是改變輸入的方向,不再使用鍵盤作爲命令輸入的來源,而是使用文件作爲命令的輸入。
 

表3:Bash 支持的輸出重定向符號
符號 說明
command <file 將 file 文件中的內容作爲 command 的輸入。
command <<END 從標準輸入(鍵盤)中讀取數據,直到遇見分界符 END 才停止(分界符可以是任意的字符串,用戶自己定義)。
command <file1 >file2 將 file1 作爲 command 的輸入,並將 command 的處理結果輸出到 file2。


和輸出重定向類似,輸入重定向的完整寫法是fd<file,其中 fd 表示文件描述符,如果不寫,默認爲 0,也就是標準輸入文件。

輸入重定向舉例

【示例1】統計文檔中有多少行文字。

Linux wc 命令可以用來對文本進行統計,包括單詞個數、行數、字節數,它的用法如下:

wc  [選項]  [文件名]

其中,-c選項統計字節數,-w選項統計單詞數,-l選項統計行數。

統計 readme.txt 文件中有多少行文本:

[c.biancheng.net]$ cat readme.txt  #預覽一下文件內容
C語言中文網
http://c.biancheng.net/
成立7年了
日IP數萬
[c.biancheng.net]$ wc -l <readme.txt  #輸入重定向
4


【實例2】逐行讀取文件內容。

#!/bin/bash

while read str; do
    echo $str
done <readme.txt

運行結果:
C語言中文網
http://c.biancheng.net/
成立7年了
日IP數萬

這種寫法叫做代碼塊重定向,也就是把一組命令同時重定向到一個文件,我們將在《Shell代碼塊重定向》一節中詳細講解。

【實例3】統計用戶在終端輸入的文本的行數。

此處我們使用輸入重定向符號<<,這個符號的作用是使用特定的分界符作爲命令輸入的結束標誌,而不使用 Ctrl+D 鍵。

[c.biancheng.net]$ wc -l <<END
> 123
> 789
> abc
> xyz
> END
4

wc 命令會一直等待用輸入,直到遇見分界符 END 才結束讀取。

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