Linux中的輸入、輸出和錯誤重定向
如果你熟悉基本的 Linux 命令,還應該學習輸入輸出重定向的概念。
我們都知道 Linux 命令的功能,它接受一個輸入,然后給你一個輸出。在這里起作用的包含一些重要的角色,我們今天就來介紹一下這些角色。
stdin, stdout 和 stderr
當你運行 Linux 命令時,有三個數據流在其中起作用:
- stdin:(Standard input,標準輸入)是輸入數據的源。默認情況下,stdin 是從鍵盤輸入的任何文本,它的流 ID(stream ID) 為 0;
- stdout:(Standard output,標準輸出)是命令的輸出結果。默認情況下,它會顯示在屏幕上,它的流 ID(stream ID) 為 1;
- stderr:(Standard error,標準錯誤)是命令產生的錯誤消息(如果有)。默認情況下,屏幕上也會顯示 stderr。它的流 ID(stream ID)是2。
這些流包含存儲在內存緩沖區(buffer memory)中的純文本數據。
把它想象成一個水流,你需要水源,比如水龍頭,用管道連接到它,可以將其存儲在水桶(文件)中,也可以給植物澆水(打印)。如果需要,還可以將其連接到另外一個水龍頭上,也就是改變水的流向(重定向)。
Linux 中也有這種重定向的概念。你可以將 stdin, stdout 和 stderr 從其原本的目標,重定向到另一個文件或命令(甚至是打印機等外圍設備)。
接下來我們來介紹一下重定向是如何工作的,以及如何使用它。
輸出重定向
第一種也是最簡單的重定向形式是輸出重定向,也稱為標準輸出重定向。
默認情況下,命令的輸出是顯示在屏幕上。比如,我使用 ls 命令列出當前目錄下的所有文件:
通過輸出重定向,可以將輸出重定向到文件。如果此輸出文件不存在,那么 shell 將創建它。
比如,我們將上述 ls 命令的輸出保存到名為 output.txt 的文本文件中:
輸出文件是預先創建的
那么這個 output.txt 文件的內容是什么呢?我們用 cat 命令來看一下:
有沒有注意到里面也包含 output.txt?將輸出重定向輸出到的文件(output.txt)是在運行預期命令之前創建的。為什么呢?因為它需要準備好輸出的目的地,輸出將被發送到該目的地。
追加而不是刪除
一個經常被忽略的問題是,如果重定向到一個已經存在的文件,shell 將首先刪除該文件。這意味著輸出文件的現有內容將被刪除,并替換為命令的輸出。
如果不想刪除原有的內容,而只是追加,那么可以使用 >> 重定向語法:
你可以在當前的 shell 會話中禁止刪除,使用 set -C
將輸出重定向到文件,可以將輸出的內容保存起來以供將來參考;另外如果輸出的內容太多,占用了大篇幅的屏幕時,將內容保存到文件,就更方便查看和分析了,這就像收集日志文件一樣。
管道重定向
在介紹 stdin 重定向之前,我們先來了解一下管道重定向,這是更加常見的,我們會經常使用管道重定向。
關于管道重定向,可以參閱我們先前的文章:Linux 中的管道是什么?管道重定向是如何工作的?
通過管道重定向,可以將命令的標準輸出發送到另一個命令的標準輸入。
我們來舉個例子,如果我們要查看當前目錄中文件的數量,可以使用 ls -1(注意是數字1,不是字母L)來顯示當前目錄中的文件:
我們知道 wc 命令用于計算文件中的行數,所以我們可以結合這個命令,如下:
使用管道,兩個命令共享相同的內存緩沖區,第一個命令的輸出存儲在緩沖區中,然后該緩沖區將用作下一個命令的輸入。
你將看到管道中最后一個命令的結果。這一點很明顯,因為先前命令的 stdout 被重定向到下一個命令,而不是打印在屏幕上。
管道重定向或管道不限于僅連接兩個命令,你也可以連接多個命令,只要一個命令的輸出可用作下一個命令輸入。
請注意,stdout/stdin是一塊數據,而不是文件名
一些新的Linux用戶在使用重定向時會感到困惑,如果命令返回一組文件名作為輸出,則不能將這些文件名用作參數。
比如,使用 find 命令查找擴展名為 .txt 的文件,無法通過管道將查找到的這些文件移動到新的目錄,不能這樣操作:
這就是為什么我們經常會看到 find 命令與 exec 或 xargs 命令組合使用的原因。這些命令可以將大量的文本“文件名”轉換為文件名,且可作為參數傳遞。
關于find 與 exec 或 xargs 命令組合使用的相關內容,可參考我們先前的文章:
find 與 exec 命令的結合,是一個功能強大的搜索工具
如何在Linux中使用xargs命令
輸入重定向
使用 stdin 重定向可以將文本文件的內容傳遞給終端命令:
但是這種并不常用,因為大多數 Linux 命令都可以接受文件名作為參數,因此通常不需要 stdin 重定向。比如:
上面的命令可以直接寫為:head filename.ext(也就是不用重定向符號 <)。
也不是說 stdin 重定向完全沒有用,有些命令是依賴于它的。以 tr 命令為例,這個命令可以做很多事情,但在下面的例子中,它將輸入文本從小寫轉為大寫:
事實上,建議在管道上使用標準輸入重定向,以避免不必要地使用 cat 命令。
比如上面的例子,很多人就習慣使用 cat,然而這里并沒有必要使用cat:
合并重定向
你可以根據需要組合使用 stdin,stdout 和管道重定向。
比如,下面的命令列出當前目錄下所有的 txt 文件,然后統計一下文件的總數,并將這個結果保存到一個新文件中:
錯誤重定向
有時候,當你運行一些命令或腳本時,會在屏幕上看到一條錯誤信息:
在本文的開頭,我們提到過有三種數據流,stderr 是其中之一,它默認將輸出顯示在屏幕上。
你也可以重定向 stderr。由于它是一個輸出數據流,因此可以使用與標準輸出重定向相同的 > 或 >> 重定向符號。
但是,當 stdout 和 stderr 都作為輸出數據流時,如何區分它們呢?通過它們的流 ID(stream ID,也稱為文件描述符)。
Data stream | stream ID |
stdin | 0 |
stdout | 1 |
stderror | 2 |
-t | -list |
-u | -update |
-x | –extract, –get |
-j | –bzip2 |
-z | –gzip, –gunzip, –ungzip |
默認情況下,當你使用輸出重定向符號 > 時,它實際上是 1 >。換句話說,ID 為 1 的數據流是在這里輸出。
所以當你要重定向 stderr 時,可以使用它的ID,比如 2> 或者 2>>。這表示輸出重定向用于數據流 stderr(ID為2)。
stderr 重定向示例
我們來舉幾個例子。假如我們只想要保存錯誤信息,那么可以這樣:
這個很簡單。我們再來個稍微復雜一點的(并且很有用的):
在上面的例子中,ls 命令嘗試顯示兩個文件的信息。其中一個文件能成功,另一個會出錯。所以我在這里做的是將 stdout 輸出重定向到 output.txt 文件中,將 stderr 重定向到 error.txt 中(使用 2>)。
此外,我們還可以將 stdout 和 stderr 重定向到同一個文件,是有辦法做到這一點的。
在下面的例子中,我使用追加模式(append mode)將 stderr 重定向到文件 combined.txt 中(使用 2>>);然后,同樣使用追加模式將標準輸出 stdout 重定向到同一個文件 combined.txt 中(使用 >>):
另一種方法,也是首選的方法,是使用類似于 2>&1 的東西,可以大致的翻譯為“將 stderr 重定向到與 stdout 相同的地址中”。
我們以前面的示例為例,這次使用 2>&1 將 stdout 和 stderr 重定向到同一個文件:
在這里需要注意的一點是,不能想當然的以為 2>>&1 為追加模式,因為 2>&1 本身就是追加模式。
此外,你也可以先使用 2>,然后使用 1>&2 將 stdout 重定向到與 stderr 相同的文件上。基本上,>& 是將一個輸出數據流重定向到另一個上。
我們來總結一下
1)有三種數據流,其中一個是輸入數據流 stdin(0),兩個輸出數據流為 stdout(1) 和 stderr(2);
2)鍵盤輸入是默認的標準輸入設備,屏幕是默認的輸出設備;
3)輸出重定向使用 > 或者 >>(用于追加模式);
4)輸入重定向使用 <;
5)stderr 可以使用 2> 或者 2>>;
6)stderr 和 stdout 可以組合為:2>&1。
至此我們了解了關于重定向的相關知識,那么為了更加深入的學習,還可以了解一下 tee 命令,該命令可以將數據顯示到標準輸出中,同時保存到文件中。我們稍后會分享這個命令的用法。
以上就是本次分享全部內容,歡迎討論。