Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

程式設計師聯盟發表於2017-04-07

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

-- 作者 謝恩銘 轉載請註明出處

內容簡介


  1. 第三部分第二課:流、管道、重定向,三管齊下
  2. 第三部分第三課預告:監視系統活動,滴水不漏

流、管道、重定向,三管齊下


上一課是 Linux探索之旅 | 第三部分第一課:資料處理,慢條斯理

這一課我們來學一些非常有用的內容,而且相當有意思,而且內容很多,而且有可能顛覆你的三《觀》(畢竟三管齊下,不顛覆三觀也難)。

今天的標題中的三個名稱,聽上去就怪怪的。什麼流,管道,重定向,都是什麼啊。不過相信學完這課,大家能夠有撥雲見霧的感覺。

到目前為止,我們已經學習了不少Linux的命令了,也已經比較熟悉命令列的用法了。其最基本用法是這樣的:

  1. 在終端輸入命令(比如輸入ls命令)。

  2. 命令的執行結果顯示在終端中。

但是我們還不知道的是:其實我們可以重定向命令的執行結果。

重定向,是什麼意思呢?簡單來說,就是我們可以把本來要顯示在終端的命令結果,輸送到別的地方:到檔案中或者作為其他命令的輸入(命令的連結,或者叫命令管道)。

把兩個命令連起來使用,一個命令的輸出作為另一個命令的輸入,這就構成了管道。

管道的英語是pipeline。你可以想象一個個水管,連線起來。這一個水管流出來的水(輸出),如果接上另一個水管,水是不是就流入另一個水管,成為另一個水管的輸入了?

當然了,在電腦科學中,流(英語是stream)的含義是比較難理解的,也比較豐富,不同的情況下含義也不太一樣。如果把它比作水流可能不完全。

知乎上就有一篇帖子,大家可以看看:如何理解程式語言中「流」(stream)的概念?

我們常可以看到這樣的字樣:位元流,位元組流,資料流,視訊流,音訊流,流媒體,流演算法,流處理,資料流挖掘,等等。

在維*基百科中,流的簡單定義是這樣的,供大家參考:

In computer science, a stream is a sequence of data elements made available over time. A stream can be thought of as items on a conveyor belt being processed one at a time rather than in large batches.

以上這段英語翻譯過來大致意思是(有點難翻譯... 翻得不好不要拍我):

在電腦科學中,流是時間上可用的一系列資料元素。我們可以把流比喻成傳送帶上的物件,每個時間點傳輸一個,而不是多個打包傳輸。

還是不太容易理解對吧?沒事,學完這課應該就理解了,因為有大量例項和圖解幫助你。

前面說了,我們會學習如何把命令的輸出結果重定向到其他地方:

  1. 哪裡:檔案或者另一個命令的輸入。

  2. 如何實現:通過在命令間插入特定的符號(這些符號可以被稱為《重定向流》符號)。下面我們會學到,有好幾種符號。

可以用下圖來做一個小結:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

可以看到,命令的輸出可以有三個去向:

  • 終端

  • 檔案

  • 其他命令的輸入

到目前為止,我們只用過第一種形式:把命令的輸出結果顯示在終端。

我們豈能就此罷休呢對吧,肯定要乘勝追擊啊。一不做二不休啊。

重定向流從Unix時代就已經是很重要的概念了,後來Linux出現,重定向流的原理依舊被沿用。

重定向流將會改變我們看待終端命令列的方式,所以這課很重要。

藉著這一課,我們將把我們的命令列的造詣提升到另一個層級。如果之前是命令列的“小學”,那麼這一課學完就基本可以“初中畢業”了。

你也許看到過一些Linux高手輸入命令,經常會輸入一大串,然後回車。這一大串命令中很可能就用到了我們今天學的流,管道和重定向。

>和>>:重定向到檔案


我們先從最簡單的開始。最簡單的操作就是把命令的輸出結果重定向到檔案中,就不會在終端顯示命令執行結果了。

準備工作:再談cut命令


在開始學習>和>>這兩個符號的用法之前,我們需要建立一些檔案。

在建立檔案之前,我們來談談cut命令的進階用法。cut是英語《剪下》的意思,用於從檔案中剪下出一部分內容。

上一課中我們學習了cut命令,但沒有深入講解,只講了基本的用法(-c引數:根據字元數來剪下)。

現在讓我們學習一下cut命令的其他引數。

cut命令進階:根據分隔符來剪下


首先,我們來看一種特殊的檔案形式:CSV格式。

CSV是Comma Separated Values的縮寫,翻成中文是《逗號分隔值》。

以下摘自百度百科:

逗號分隔值(Comma-Separated Values,CSV,有時也稱為字元分隔值,因為分隔字元也可以不是逗號),其檔案以純文字形式儲存表格資料(數字和文字)。
純文字意味著該檔案是一個字元序列,不含必須像二進位制數字那樣被解讀的資料。
CSV檔案由任意數目的記錄組成,記錄間以某種換行符分隔;每條記錄由欄位組成,欄位間的分隔符是其它字元或字串,最常見的是逗號或製表符。
通常,所有記錄都有完全相同的欄位序列。

CSV檔案的字尾名是.csv,通常可以被Excel等軟體開啟,開啟之後會把分隔符隔開的各個數值填充到表格裡。

我們來構建一個CSV檔案。因為我們要用逗號作為分隔符,來學習cut命令的進階使用:根據分隔符來剪下。

假設我們有一個人數不多的班級,作為老師我們需要統計新近一次考試的成績。我們為此製作了一個表格,並按照順序把資料寫入表格。

我們的CSV檔案的內容可以如下,我們用Nano這樣的文字編輯器來編寫這個CSV檔案,並且取名:notes.csv (note是英語《成績》的意思)。

Mack,95 / 100,很不錯
Matthew,30 / 100,跟平時一樣水
Louise,70 / 100,有進步
Luke,54 / 100,接近平均分了
John,68 / 100,不錯,但還可以更好
Samuel,100 / 100,總是那麼完美
David,40 / 100,退步挺大呀複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

上面的“跟平時一樣水”中的“水”表示“不怎麼樣,很一般,有點糟糕”。

如上圖所示,我們的notes.csv檔案中每一行由三部分組成,每部分由一個逗號分隔:

  • 學生名字

  • 成績(滿分是100分)

  • 評語

現在假如我們要從notes.csv檔案中提取名字那一列,怎麼辦呢?我們不能用cut命令的-c引數啊,畢竟每個名字的字元數不相等。

聰明如你應該想到了。是的,我們看到檔案中每一行的每一部分是用分隔符來隔開的,所以我們可以這樣做:

需要用到兩個引數:

  • -d引數:d是delimiter的縮寫,是英語《分隔符》的意思。用於指定用什麼分隔符(比如逗號,分號,雙引號等等)。

  • -f引數:f是field的縮寫,是英語《區域》的意思。表示剪下下用分隔符分隔的哪一塊或哪幾塊區域。

我們的notes.csv檔案是用逗號來分隔三個部分的,我們要剪下下來的是名字那一列,也就是第一部分。因此我們可以這樣使用:

cut -d , -f 1 notes.csv複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

怎麼樣,很不錯吧。我們通過cut命令的兩個引數就實現了從notes.csv檔案中剪下下第一部分(名字)的想法。

那如果我們只想剪下下評語部分呢?評語是第三部分:

cut -d , -f 3 notes.csv複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

那如果我們要第一和第三部分呢?可以這樣:

cut -d , -f 1,3 notes.csv複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

同樣地,我們可以用

cut -d , -f 2- notes.csv複製程式碼

來剪下第二部分直到最後的內容:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

好了,現在準備工作做好了,我們可以來學習重定向流符號了。

>:重定向到新的檔案


我們知道雖然我們剛才用cut命令從notes.csv檔案中剪下出來一些部分,但原始的notes.csv檔案是不變的。

我們現在想要將剪下出來的部分儲存到一個檔案中,而不是像之前那樣顯示在終端裡。

(為了方便演示,我們在家目錄下新建一個redirect目錄,redirect是英語《重定向》的意思。將之前的notes.csv檔案放到這個目錄下,再用cd redirect命令定位到這個目錄)

cd ~     # 其實單獨用cd也可以定位到使用者的家目錄
mkdir redirect
cd redirect複製程式碼

我們需要用到>這個神奇的符號。如果你是美式鍵盤,那麼可以在句號那個鍵找到這個符號,在句號的上面。所以要輸入這個符號,可以用Shift加上句號那個鍵。

做網路前端開發的程式設計師應該對這個符號不陌生,因為HTML語言裡到處是<>這對符號。

這個符號可以將命令的輸出結果重定向到你自己選擇的檔案中。例如:

cut -d , -f 1 notes.csv > students.txt複製程式碼

student是英語《學生》的意思,students就是複數啦。

如果你執行上述命令,那麼終端不會有任何顯示。因為我們將cut命令的執行結果(剪下了名字那一列)重定向到students.txt檔案中了。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

可以看到,我們用cat命令列印了student.txt檔案的內容,正是cut -d , -f 1 notes.csv 這句命令的執行結果。

我們用ls命令檢視redirect目錄下的檔案,發現多了一個students.txt檔案。

怎麼樣,>這個符號很有用吧。

不過使用時要小心,因為>符號會把輸出重定向到檔案中,如果此檔案不存在,則新建一個檔案;
如果此檔案已經存在,那就會把檔案內容覆蓋掉(清除原有內容,然後寫入檔案),而且是不會徵求使用者確認的!

有時候,我們既不想將命令的輸出顯示在終端,又不想將其儲存到檔案中,怎麼辦呢?

Linux中有一個俗稱《黑洞》的檔案,就是

/dev/null複製程式碼

null是英語《無,空》的意思。

/dev/null 檔案是特殊檔案,不是一個目錄。此檔案具有唯一的屬性:它總是空的。它能使傳送到/dev/null 的任何資料作廢,就好像這些資料掉進了無底的黑洞一般。

因此,假如我們不需要在終端顯示剛才那個cut命令的結果,也不想儲存到檔案裡,那麼可以這麼做:

cut -d , -f 1 notes.csv > /dev/null複製程式碼

然後,整個世界都清淨了...

>>: 重定向到檔案末尾


我們已經知道,單獨一個>符號可以實現重定向到新的檔案(覆蓋檔案內容),那麼兩個連在一起的>符號有什麼作用呢?

>>的作用與>是類似的,不過它不會像>那麼危險(如果檔案已經存在,>符號會覆蓋檔案內容),而是將重定向的內容寫入到檔案末尾,起到追加的作用。如果檔案不存在,也會被建立。

我們就來實踐一下:

cut -d , -f 1 notes.csv >> students.txt複製程式碼

因為我們上一個例子中已經用>符號來重定向名字那列的內容到students.txt檔案中了,所以上面的命令會追加同樣內容到students.txt的末尾。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

我們用cat命令列印了students.txt檔案的內容,可以看到有兩遍notes.csv中名字列的內容。

>>符號在很多情況下非常有用,比如你人不在電腦前,而你又想讓終端為你記錄程式執行的結果,就可以在一個日誌檔案的末尾一直寫入。例如:

command >> results.log複製程式碼

小結


我們方才學習了兩個重定向流符號:

  • >:重定向到檔案中。如果檔案已存在,則覆蓋檔案內容;檔案不存在,則建立檔案。

  • >>:重定向到檔案末尾。如果檔案不存在,則建立檔案。

可以用下圖演示兩個符號的原理:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

2>,2>>,2>&1:重定向錯誤輸出


怎麼樣,流的重定向是不是很有趣?那麼我們繼續學習其他的重定向流符號。

我們首先來學點新知識。

stdin,stdout,stderr:標準輸入,標準輸出,標準錯誤輸出


這三個又是什麼呢?這一課新東西果然多。

一般學程式設計,到某個階段,總應該會碰到這三位“仁兄”的。既然逃不了,不如勇敢來面對。

對於我們的終端命令列,我們從鍵盤向終端輸入資料,這是標準輸入,也就是stdin。

終端接收鍵盤輸入的命令,會產生兩種輸出:

  • 標準輸出:stdout。指終端輸出的資訊(不包括錯誤資訊)。

  • 標準錯誤輸出:stderr。指終端輸出的錯誤資訊。

第一個stdout就是我們到目前為止看到的那些Linux命令的正常執行結果,比如在終端中執行ls命令,我們以前也看到了它列出當前目錄下所有檔案。

那什麼是標準錯誤輸出呢?其實我們以前也看到過,只是見得不多而已。

我們用一個例子來說明。

假設,我們執行cat notes.csv命令,想要顯示notes.csv檔案的內容。將會有兩種結果:

如果一切順利(notes.csv檔案存在於當前目錄,而且我們有許可權這麼做),那麼終端就會顯示其內容。這是標準輸出。

如果出錯(比如notes.csv檔案不存在),那麼終端就會顯示錯誤資訊。這是標準錯誤輸出。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所示,我們在redirect這個目錄中執行cat notes.csv時,因為檔案存在,而且我們有許可權對其執行cat命令,所以列印出了檔案內容。這就是標準輸出。

接著,我們用cd .. 命令定位到了上一級目錄,也就是使用者的家目錄,在這個目錄中我們並沒有建立notes.csv檔案,所以cat notes.csv命令自然就輸出了錯誤資訊:No such file or directory,翻譯過來就是《不存在此檔案或目錄》。這就是標準錯誤輸出。

預設情況下,標準輸出和標準錯誤輸出都會顯示在終端,這也是為什麼我們之前對它們的區別並沒有那麼在意的原因,因為長得挺像的,都在終端輸出。

我們可以用下圖來演示:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

這三個你也可以把它們看作流:

  • stdin:標準輸入流。英語standard input的縮寫(standart是英語“標準”的意思,input是英語“輸入”的意思)。標準輸入是指輸入至程式的資料(通常是檔案)。程式要求以讀(read)操作來傳輸資料。並非所有程式都要求輸入。如dir或ls程式執行時不用任何輸入。 除非重定向,輸入是預期由鍵盤獲取的。 標準輸入的檔案描述符為 0 (零);POSIX 的定義是 STDIN_FILENO;相對應的 變數為 FILE* stdin 。

  • stdout:標準輸出流。英語standard output的縮寫(output是英語“輸出”的意思)。標準輸出是指程式寫輸出資料的流。程式要求資料傳輸使用寫的運算。並非所有程式都要求輸出。如mv或ren程式在成功完成時是沒有輸出的。 除非重導向,輸出是預期顯示在終端上的。 標準輸出的檔案描述符為 1 (一)。POSIX 定義是 STDOUT_FILENO;相對應的 變數為 FILE* stdout 。

  • stderr:標準錯誤輸出流。英語standard error的縮寫(error是英語“錯誤”的意思)。標準錯誤輸出是另一個輸出流,用於輸出錯誤訊息或診斷。它獨立於標準輸出,且標準輸出和標準錯誤輸出可以分別被重定向。標準錯誤輸出的檔案描述符為 2 (二);POSIX 定義為 STDERR_FILENO;相對的 變數 FILE* stderr。

檔案描述符 名字 解釋
0 stdin 標準輸入
1 stdout 標準輸出
2 stderr 標準錯誤輸出

那什麼是檔案描述符呢?

檔案描述符的英語是File Descriptor,簡稱fd。File是英語“檔案”的意思,而“Descriptor”是英語“描述符”的意思。

檔案描述符是電腦科學中的一個術語,要完全講清楚可能要用單獨的一課。我們就不深究了。

檔案描述符是一個用於表述指向檔案的引用的抽象化概念。這定義本身也有點抽象。我們不需要太深入瞭解。大致只需要知道:

檔案描述符在形式上是一個非負整數。
實際上,它是一個索引值,指向作業系統核心為每一個程式所維護的該程式開啟檔案的記錄表。
當程式開啟一個現有檔案或者建立一個新檔案時,核心向程式返回一個檔案描述符。

檔案描述符通常是Unix,Linux等系統的概念。在Windows中,也有類似的概念,但是Windows中稱為《控制程式碼》,就是handle。

好了,重新回到我們的話題。剛才我們已經學習了用>和>>兩個符號可以將輸出重定向到檔案中,那麼我們在出錯的情況下是不是也可以用>和>>將標準錯誤輸出也重定向到檔案中呢?我們來試試:

cat not_exist_file.csv > results.txt複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所示,因為 not_exist_file.csv 這個檔案不存在(正如其名),所以產生了錯誤資訊。但是這個錯誤資訊卻並沒有如我們所願的寫入到results.txt檔案中,而是仍舊在終端輸出了。這是為什麼呢?不是說好的>符號用於重定向輸出到檔案的嗎?

其實,>和>>符號只是將標準輸出重定向到檔案,並不能將標準錯誤輸出重定向到檔案。

那麼我們要重定向標準錯誤輸出,該怎麼辦呢?

我們就要用到 2> 這個符號,是的,就是在>這個符號左邊緊挨著寫一個2。

為什麼是2呢?記得上面說的嗎?標準錯誤輸出的檔案描述符是2,所以這裡的2表示標準錯誤輸出。如果沒有2,單獨的>符號就是重定向標準輸出(檔案描述符為1)。

我們補充一下剛才的命令:

cat not_exist_file.csv > results.txt 2> errors.log複製程式碼

這個命令裡有兩個重定向:

  • > results.txt:將標準輸出重定向到results.txt檔案中。

  • 2> errors.log:將標準錯誤輸出重定向到errors.log檔案中。

也就是說:

  • 假如not_exist_file.csv這個檔案確實存在,將其內容寫入results.txt檔案中
  • 假如not_exist_file.csv這個檔案不存在,將錯誤資訊寫入errors.log檔案中。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所示,我們執行了cat not_exist_file.csv > results.txt 2> errors.log這個命令,因為not_exist_file.csv這個檔案不存在,所以results.txt檔案是空的,而errors.log檔案的內容是錯誤資訊。

類似地,2>>符號用於將標準錯誤輸出重定向到檔案末尾。

合併輸出


上面我們學習瞭如何將標準輸出和標準錯誤輸出分別重定向到不同檔案。但是有的時候,我們比較“任性”,我們就想把標準輸出和標準錯誤輸出都重定向到同一個地方。怎麼做呢?

須要使用 2>&1 這個組合符號。

看著怪怪的對吧?由四個字元組成。這個符號的作用是:將標準錯誤輸出重定向到與標準輸出相同的地方。

我們用例項演示一下:

cat not_exist_file.csv > results.txt 2>&1複製程式碼

上面的命令的作用是:將cat not_exist_file.csv這個命令的所有輸出(標準輸出和標準錯誤輸出)都重定向到results.txt檔案中。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所示,執行cat not_exist_file.csv > results.txt 2>&1命令之後,因為not_exist_file.csv這個檔案不存在,但又因為加了2>&1這個符號,所以標準輸出(為空)和標準錯誤輸出(cat: not_exist_file.csv: No such file or directory)都重定向到results.txt檔案中了。

大家是否覺得要將標準輸出和標準錯誤輸出都重定向到檔案末尾,應該是這樣寫:2>>&1 呢?

其實不然,這樣是不對的。我們還是保持2>&1這個組合不變,只改變前面的符號就行了。例如:

cat not_exist_file.csv >> results.txt 2>&1複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

可以看到,又追加了一條錯誤資訊到results.txt檔案中。

小結


  • 2>:將標準錯誤輸出重定向到檔案。如果檔案已經存在,則覆蓋檔案內容;如果不存在,則建立檔案。

  • 2>>:將標準錯誤輸出重定向到檔案末尾。如果檔案不存在,則建立檔案。

  • 2>&1:將標準輸出和標準錯誤輸出都重定向到一個地方。

用下圖來演示:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

上圖中,我故意沒有加>>和2>>,不然這圖就太複雜了。

其實>和>>,2>和2>>的區別就是前者覆蓋檔案內容,後者追加內容到檔案。

<,<<:從檔案或鍵盤讀取


到目前為止,這一課我們只講瞭如何重定向命令的輸出,也就是決定命令輸出的資訊的去向。那麼接著我們可以做一點相反的事情:決定命令的輸入來自哪裡。

當然了,上面也說了,不是所有的命令都有輸入,也不是所有的命令都有輸出。

到目前為止,我們的命令的輸入都來自於後面接的引數,這些引數有些是檔名,有些是目錄名,等等。

但我們其實可以使命令的輸入來自於檔案或者鍵盤輸入。如下圖所示:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

<:從檔案中讀取


看到這個<符號,是不是想到了之前的>符號呢?

是的,這對“孿生兄弟”,原理類似但是功能正相反。<符號用於指定命令的輸入。

我們用一個簡單的例子來演示:

cat < notes.csv複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所示,cat < notes.csv的執行結果和cat notes.csv (不用重定向流符號)一模一樣,都是在終端列印notes.csv的內容,那我們為什麼需要<符號呢?

事實上,雖然cat < notes.csv的執行結果和cat notes.csv一樣,但是原理卻不一樣:

  • cat notes.csv :這種情況下,cat命令接受的輸入是notes.csv這個檔名,那麼它要先開啟notes.csv檔案,然後列印出檔案內容。

  • cat < notes.csv :這種情況下,cat命令接受的輸入直接是notes.csv這個檔案的內容,cat命令只負責將其內容列印。而開啟檔案並將檔案內容傳遞給cat命令的工作則交給shell程式(也就是控制終端的程式)來完成。

所以,雖然結果看似一樣,但是中間的過程卻是不一樣的。

<<:從鍵盤讀取


<<符號的作用是將鍵盤的輸入重定向為某個命令的輸入,很多情況下都很有用。

我們用例項來說明:

sort -n << END複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所示,輸入這條命令之後,回車,終端就進入了鍵盤輸入模式。

看到那個>符號和其後閃動的游標了麼?就是讓你輸入資料的。

我們知道sort -n的作用是按照從小到大進行排列。那麼我們就輸入一些數值吧(每輸一個數值,用Enter鍵來換行,接著輸入下一個數值,輸入END來結束輸入,END被稱為結束字串。當然了,你可以用其他字串,比如haha,nihao,不一定要用END。我用END是因為end是英語“結束,結尾”的意思):

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

可以看到,sort -n命令將我們輸入的一串數值進行了由小到大的排序。

我們再試試其他命令與<<符號的配合,這次我們用wc命令吧,wc命令用於統計字元等,如果配合-m引數就是統計字元數。

wc -m << END複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

可以看到,《How many characters are there in this sentence ?》這句話中有49個字元。

這句話翻成中文就是《這句話中有多少個字元?》

小結


  • <:將命令的輸入重定向為檔案內容。

  • <<:將命令的輸入重定向為鍵盤輸入,以逐行輸入的模式(Enter鍵換行)。所有輸入的行都將在輸入結束字串(例如上面例子中的END)之後傳送給命令。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

當然了,我們也可以將之前學習的輸出重定向符號和這一節的輸入重定向符號結合使用:

sort -n << END > numbers_sorted.txt 2>&1複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所見,以上命令將sort -n命令對數值排序的結果都輸入到numbers_sorted.txt檔案中,如果有錯誤資訊也輸入。

|:管道


好了,終於來到本課的最後一節了。

是不是有點累呢?確實,這課內容比較多,也比較難。

如果累的話,可以去烤一隻烤鴨。

為什麼小編不像以前一樣說烤雞了呢?是因為有一對好友夫婦在法國Aix en Provence開了一家烤鴨店,他們的公眾號發了幾張誘人的烤鴨照片,看得小編直流哈喇子。
但是吃不到啊,小編住在巴黎,離Aix en Provence有近7個小時火車的路程。唉,以後去吧~

好了,不扯烤鴨了,言歸正傳。

這一節要學的符號,將會成為你以後Linux生涯中最常用的符號之一,使用率絕對高過這一課學的其他符號,因此作為“壓軸曲目”非常合適。

這個符號就是所謂的《管道符號》:

|複製程式碼

是的,就是美式鍵盤上位於反斜槓那個鍵的那個符號,要輸入這個符號,需要使用Shift + \

| 符號既然被稱為《管道符》,那麼其作用就是《建立命令管道》咯。

是的,還記得本課開篇的時候我們提到的那個比喻嗎?一個命令的輸出可以作為另一個命令的輸入,就好像將兩根水管接起來一樣,前一根水管流出來的水就會流入後一根水管了。

管道也算是重定向流的一種。

原理


將兩個命令連成管道,這是什麼意思呢?簡單的說就是將一個命令的輸出作為另一個命令的輸入,如下圖所示:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

大致來說:命令1的輸出立即會變成命令2的輸入。使用這個原理,我們可以用 | 符號連線無窮多個命令,構成很長的命令管道。

管道符絕對使Linux命令的威力增加N倍。之前我們也說了,Linux中的命令(很多都是從Unix時代承襲下來的),每一個的功能雖然有限,但是卻在它們自己的崗位上盡忠職守,工作做得棒棒的。

所以單獨一條Linux命令可能功能有限,但是一旦“鐵索連環”,那可是會結合各個命令的功能,其強大不難想見。

實踐


我們用幾個例項來學習管道吧。

按學生名字排序


你應該還記得我們的notes.csv檔案,其中有三部分,用逗號隔開的,第一部分是學生名字,第二部分是成績,第三部分是評語。

之前我們用cut -d , -f 1 notes.csv 命令來剪下了名字那一列。我們前一課學了sort命令,它用於排序檔案內容。

那麼為什麼不把這兩個命令用管道符連線起來呢?我們可以用sort命令對cut命令提取到的名字列進行排序:

cut -d , -f 1 notes.csv | sort複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

如上圖所見,我們用sort命令對名字列按照首字母的字典順序進行了排序。

怎麼樣,管道是不是很有意思?

如果我們將上面的命令再擴充一下,配合之前學習的輸出重定向符號,就變成了這樣:

cut -d , -f 1 notes.csv | sort > sorted_names.txt複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

可以看到,我們將sort命令排序的結果重定向到sorted_names.txt檔案中了。

根據大小排序目錄


之前我們學過,du命令可以深入遍歷當前目錄下每個子目錄,把所有檔案的大小都做一個統計。

我們可以用cd命令來回到我們的家目錄。然後執行du命令。

問題是:du命令要執行挺久的,因為小編家目錄下檔案很多。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

單獨用du命令的缺點我們也有目共睹了,一是可能要執行很久(如果檔案很多),二是顯示結果沒有排序,雜亂無章。

但如果我們這樣做就會清爽很多:

du | sort -nr | head複製程式碼

還記得head命令的用法麼?如果不用-n引數指定顯示行數,那麼head會預設顯示前10行。

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

所以以上命令的作用是:

  • du :深入遍歷當前目錄下每個子目錄,把所有檔案的大小都做一個統計。

  • sort -nr :sort命令的-n引數是按以數值來排序(此處是檔案大小)排序,預設是小的在前;-r引數是倒序排列,有了-r引數,-n引數就變成大的數值在前了。

  • head :列出前十個數值(這裡是檔案大小)。

列出包含關鍵字的檔案


還記得我們的好朋友grep命令嗎?之前的課中有學過,這個命令很強大,可以在檔案中查詢關鍵字,並且顯示關鍵字所在的行。但有時,我們會覺得grep顯示的資訊太冗長了。每一行不僅有檔名,還有關鍵字出現的那一行文字,等等。

我們可以執行以下命令試試:

sudo grep log -Ir /var/log | cut -d : -f 1 | sort | uniq複製程式碼

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

這個命令做了什麼呢?讓我們一步步來分解:

  • sudo grep log -Ir /var/log :遍歷/var/log這個目錄及其子目錄,列出所有包含log這個關鍵字的行。-I引數用於排除二進位制檔案。-r引數用於遞迴遍歷。sudo命令是為了以root身份查詢系統資料夾/var/log。

  • cut -d : -f 1 從命令1的輸出結果中只剪下出檔名那一列(由冒號分隔的第一個區域)。

  • sort 將檔名的列以首字母的字典順序進行排序。

  • uniq : 去掉重複的檔名。

小結


小結很簡單,因為管道符的基本作用比較簡單,就是將一個命令的輸出重定向為另一個命令的輸入。如下圖所示:

Linux 探索之旅 | 第三部分第二課:流、管道、重定向,三管齊下

記住這個基本原理就好了。

好了,這一課終於結束了。是不是有點暈?如果覺得還沒怎麼掌握,那需要再讀一遍本課程,參考一些課外讀物,自己在終端上多練習。

盡情發揮你的想象力,來創造出各種命令的組合吧。

相信經過這一課的“洗禮”,您的“三觀”應該更加正了吧。

總結


  1. Linux命令的結果(標準輸出),我們不一定要顯示在終端裡,也可以將其存放在一個檔案裡,只需要在命令後加上 > 符號,然後接檔名就可以了。例如 ls > file_list.txt 就會把ls命令的結果(當前目錄所有檔案的列表)存放到file_list.txt檔案中,不在終端顯示了。

  2. >> 符號可以追加內容。>符號會把檔案內容清空,再寫入。如果檔案已經存在,那麼>>符號會在檔案末尾追加寫入內容。

  3. 2>和2>>符號用於重定向標準錯誤輸出到檔案中。2>&1符號用於將標準錯誤輸出和標準輸出重定向到相同地方。

  4. <符號重定向命令的輸入為檔案內容,<<符號重定向命令的輸入為鍵盤輸入。

  5. 管道符號 | 可以將命令連線起來,好像一個個對接的管道一樣,前一個命令的輸出成為後一個命令的輸入。

第三部分第三課預告


今天的課就到這裡,一起加油吧!

下一課我們學習:Linux探索之旅 | 第三部分第三課:監視系統活動,滴水不漏


微信公眾號「程式設計師聯盟」ProgrammerLeague
我是謝恩銘,在巴黎奮鬥的軟體工程師。
我的簡介
我的經歷
熱愛生活,喜歡游泳,略懂烹飪。
人生格言:“向著標杆直跑”

相關文章