shell中默認有三個標准設備:標准輸入(STDIN)、標准輸出(STDOUT)、標准錯誤(STDERR)。
在Linux系統中,一切(或幾乎一切)都是文件。因此,標准輸入的文件描述符是0,標准輸出的文件描述符是1,標准錯誤的文件描述符是2。
shell命令的輸出默認顯示在終端顯示器上,示例如下:
$ echo hello world
hello world
可以使用輸出重定向符號把標准輸出重定向到一個文件,示例如下:
$ echo hello world > log.txt
$ cat log.txt
hello world
標准輸出的文件描述符為1,該值為輸出重定向符號的默認值,可以省略。因此,上面的示例等效為:
$ echo hello world 1> log.txt (這里的 1 與 > 符號之間不能有空格)
$ cat log.txt
hello world
當然我們也可以使用輸出重定向符號對標准錯誤進行重定向,但必須在 > 符號前明確指定標准錯誤的文件描述符,即使用 2> 對標准錯誤進行重定向。
既然提到了輸出重定向,就必須說明輸出重定向符號有兩種:> 符號 和 >> 符號。區別如下:
- > file:打開file文件時會先清空文件,然后添加輸出信息。
- >> file:打開file文件時不清空文件,直接在file文件結尾處添加輸出信息。
示例如下:
$ cat log.txt
hello world
$ >> log.txt
$ cat log.txt
hello world
$ > log.txt
$ cat log.txt
$
如果願意,可以將STDOUT和STDERR輸出重定向到同一個輸出文件。為此,bash shell提供了一個特殊的重定向符號,即 &> 符號。
使用 &> 符號,命令生成的所有輸出都發送到同一位置,包括數據和錯誤。而且bash shell自動使錯誤信息的優先級高於標准輸出,這樣你就可以一起查看錯誤信息,而不用在整個輸出文件中查找。貌似對cat命令的輸出不起作用。
重定向到某個文件描述符
重定向到某個文件描述符時,必須在文件描述符前面添加 & 符號。必須這樣的原因:因為我們知道類似1,2這樣的文件描述符也是標准的Linux文件名稱,添加 & 符號以做區分。
這樣一來,我們經常在腳本中見到的 2>&1 命令是不是很好理解了?是的,就是把標准錯誤重定向到標准輸出。這不是廢話嘛,標准錯誤默認就跟標准輸出在同一個位置━━終端顯示器。對的,使用終端顯示器作為輸出設備時是這樣的,如果我們要把輸出重定向一個文件中時,我們就要使用 2>&1 命令了。說到這里,問題來了,如下:
command > file 2> file 與 command > file 2>&1 效果一樣嗎?
效果貌似一樣:因為不管是command產生的標准輸出信息還是標准錯誤信息都重定向到了file文件里。確實如此,但也有讓人意料之外的地方:
command > file 2> file 命令把STDOUT和STDERR都直接送到file文件中,file文件會被打開兩次,這樣STDOUT和STDERR會相互覆蓋。該命令執行時相當於兩個進程同時向同一個文件中寫數據,你寫你的,我寫我的,也不進行同步,寫完拉倒。打開文件一看,數據重疊,亂七八糟。示例如下:
$ cat badfile log.txt > log 2> log
$ cat log
hello world
: No such file or directory
$
command > file 2>&1 命令把STDOUT直接送往file文件,而STDERR經由STDOUT的通道把數據信息送到file文件中。此時,file文件只被打開了一次,因此標准輸出數據和標准錯誤數據不會相互覆蓋,而是井然有序。示例如下:
$ cat badfile log.txt > log 2>&1
$ cat log
cat: badfile: No such file or directory
hello world
$
從I/O效率上來說,command > file 2> file 相比於 command > file 2>&1 要低,而且會出現數據相互覆蓋的情況。因此,我們一般會使用后面這條命令。
在同一個命令中多次進行輸出重定向
如果我們在同一個命令中進行了多次輸出重定向操作,會出現什么情況呢?最終命令的輸出會重定向到最后一次重定向的位置。讀起來很拗口,但實際操作一下就明白了。
$ ls (空目錄)
$ tty (查看終端顯示器名稱)
/dev/pts/7
$ echo hello world > log.txt 1>/dev/pts/7 2>&1
hello world
$ ls
log.txt
$ cat log.txt
$
echo hello world > log.txt 1>/dev/pts/7 2>&1 命令首先把標准輸出重定向到log.txt文件,由於當前目錄下並不存在該文件,因此會創建該文件,並把文件清空;接着命令又把標准輸出重定向到了終端顯示器;最后又把標准錯誤重定向到了標准輸出。因此命令的輸出還是被發送到了終端顯示器上,命令結束,文件關閉,結果log.txt文件里什么數據都沒有寫入,只是創建了一個空白的文件。
最后說明
最后要說明的是,使用 > 符號進行重定向時,shell並不總是輕松瀟灑地清空重定向文件,比如/proc目錄下的文件,shell一般會檢查要寫入的數據,如果要寫入的數據或者寫入的方式有問題,shell可能會提醒你,也可能不提醒你。
有提示的示例如下:
root@lj:~# cat /proc/sys/net/ipv6/route/max_size
4096
root@lj:~# echo hello > /proc/sys/net/ipv6/route/max_size
-bash: echo: write error: Invalid argument
root@lj:~# cat /proc/sys/net/ipv6/route/max_size
4096
root@lj:~#
沒有提示的示例如下:
# cat /proc/sys/net/netfilter/nf_conntrack_udp_timeout
180
# echo hello > /proc/sys/net/netfilter/nf_conntrack_udp_timeout
# cat /proc/sys/net/netfilter/nf_conntrack_udp_timeout
180
#
因此,即便有時候某條命令執行完成后沒有任何提示信息,貌似成功了,我們還是應該檢查一下相關數據或文件,看看是否真的執行成功了。
