重定向、管道和xargs指令都是在指令之間傳遞資料的方式。Linux的最初的理念就是KISS,“Keep It Simple, Stupid!”的縮寫。指令行指令都隻做一樣事情,但是要完成得非常出色。給我最深刻印象是tar指令。tar指令的目的隻是将幾個檔案連接配接在一起組成一個大檔案而已。而壓縮的程式隻能壓縮單個檔案。肯定有人會有疑問,tar指令的-z和-j之類的參數也可以壓縮啊。其實tar本身沒有壓縮代碼,tar是通過調用外部的gzip、bzip2之類的壓縮指令來完成工作的。KISS的理念得到了廣泛的支援。好多Linux指令都隻會做一件事情。而複雜的事情則需要通過各種指令的組合來完成。各指令之間的通信,除了中間臨時檔案以外,Linux還提供了管道來完成直接的資料傳輸。重定向可以将标準輸入、标準輸入或标準錯誤重定向到檔案或裝置(這裡涉及到Linux另外一條設計理念,就是一切都是檔案,裝置都是檔案)。xargs可以将标準輸入的内容轉變成指令的指令行參數。
重定向
在講重定向之前,我想說說什麼叫标準輸入,标準輸出和标準錯誤。在Linux裡,程序間通信(IPC)最基本的一種方法是通過管道(其他還有消息、共享記憶體等)。管道就像流水線一樣,單向的,不用關心管道另外一邊是誰。系統給每一個指令行程式預設三條管道與使用者互動,就是标準輸入,标準輸出和标準錯誤。标準輸入指的是使用者按鍵盤給程式輸入資料。标準輸出是指程式正常列印給使用者的資訊,是列印到shell上的。标準錯誤跟标準輸出在本質上是相同的,都是輸出資訊給使用者。不同的是,兩個管道是分開的,可以用來列印不同種類的資訊,比如說标準錯誤通常用來列印錯誤資訊。因為使用管道不用關心另外一邊是誰,是以把管道接到别的地方,程式也不會知道。Linux用了這種特性來實作了管道的重定向。
預設情況,三個預設管道是跟使用者互動的。因為在Linux下,每個指令都隻做一件事情。有些情況下,使用者想讓程式跟程式之間互動通過預設管道互動,比如說想把一個指令的輸出接到另外一個指令的輸入裡面。重定向的方法如下:
> filename,重定向标準輸出到檔案,覆寫檔案。
>> filename,重定向标準輸出到檔案,追加模式。
2> filename,重定向标準錯誤到檔案。
&> filename,重定向标準輸入和标準錯誤到檔案。相當于同時使用>和2>。(感謝yszzf友情提供)
< filename,重定向标準輸入自檔案
舉個例子,在用find的時候,有的目錄不允許通路,find就輸出很多行的錯誤,真正的搜尋結果就看不到了。這時我會把标準錯誤重定向到空裝置去。
1. find / -name "filename" 2> /dev/null
再比如在用gdb進行自動測試的時候,把指令寫到一個檔案裡,然後送入gdb。
1. gdb program < test_script.gdb
管道
管道的原理上面基本介紹清楚了。管道是讓使用者将不同程式間的标準輸入、标準輸出連接配接起來,而不單單是重定向到檔案。用法是在指令間用“|”連接配接起來。
app1 | app2
我最常用的幾個情況:
1. 程式輸出太多,用less慢慢看。
1. verbose_app | less
2. 在程式輸出裡面進行搜尋。一種方法可以送到less裡面在用less搜尋。這裡用另外一個指令,grep。
verbose_app | grep pattern
# pattern是被搜尋的内容。如果有空格,用雙引号括起來。如果想使用擴充正規表達式,用grep -E。
3. 因為在重定向了以後,被重定向的管道就不再列印到螢幕了。如果又想列印到檔案,又想列印到螢幕,可以用tee指令
verbose_app | tee filename | another_app
xargs
如果後面的程式必須要指令行參數,而不接受管道傳參數,就要使用xargs。
1. 先示範一下原理。
echo "arg1 arg2" | xargs app
# 相當于
app arg1 arg2
2. 用find或者ls查找檔案,然後将所有檔案tar了。
find . -type f | xargs tar -cvzf output.tar.gz
3. 計算源檔案行數。這裡要用到一個叫wc的指令,是用來計算檔案單詞數、字元數和行數的。如果管道到wc的話,wc會将資料當做文本來進行計算。檔案名隻有從指令行傳遞
find .
−name"∗.c"−o−name"∗.h"−o−name"∗.cpp"−o−name"∗.hpp"
-type f | xargs wc -l
# 或者
find . -type f | grep -E "\.[ch](pp)?$" | xargs wc -l
重定向的>,2>,<之類的原理是把标準輸入等用新的管道來取代。
我一開始是這樣寫的
command >/dev/null 2>&1
這樣寫的問題是,>已經将标準輸出用null裝置替換了,然後2>&1将标準錯誤接到“标準輸出”上,而“标準輸出”現在已經變成了null裝置,也就是說标準錯誤接到了null裝置上。
正确的方法是
command 2>&1 >/dev/null