開始對重定向這個概念感到些許陌生,但通過前面的課程中多次見過>
或>>
操作了,並知道他們分別是將標准輸出導向一個文件或追加到一個文件中。這其實就是重定向,將原本輸出到標准輸出的數據重定向到一個文件中,因為標准輸出(/dev/stdout
)本身也是一個文件,我們將命令輸出導向另一個文件自然也是沒有任何問題的。
下面簡單的回顧一下前面經常用到的兩個重定向操作:
$ echo 'hello shiyanlou' > redirect
$ echo 'www.shiyanlou.com' >> redirect
$ cat redirect
當然前面沒有用到的
<
和<<
操作也是沒有問題的,如你理解的一樣,它們的區別在於重定向的方向不一致而已,>
表示是從左到右,<
右到左。
在更多了解 Linux 的重定向之前,需要先知道一些基本的東西,前面已經提到過 Linux 默認提供了三個特殊設備,用於終端的顯示和輸出,分別為stdin
(標准輸入,對應於你在終端的輸入),stdout
(標准輸出,對應於終端的輸出),stderr
(標准錯誤輸出,對應於終端的輸出)。
0
/dev/stdin
標准輸入
1
/dev/stdout
標准輸出
2
/dev/stderr
標准錯誤
文件描述符:文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞著文件描述符展開。但是文件描述符這一概念往往只適用於 UNIX、Linux 這樣的操作系統。
另外還有一個符號-
,它可以同時作為前一個命令的。
可以這樣使用這些文件描述符:
默認使用終端的標准輸入作為命令的輸入和標准輸出作為命令的輸出
$ cat
(按Ctrl+C退出)
將cat的連續輸出(heredoc方式)重定向到一個文件
$ mkdir Documents
$ cat > Documents/test.c\~ <<EOF
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
EOF
將一個文件作為命令的輸入,標准輸出作為命令的輸出
$ cat Documents/test.c\~
將echo命令通過管道傳過來的數據作為cat命令的輸入,將標准輸出作為命令的輸出
$ echo 'hi' | cat
將echo命令的輸出從默認的標准輸出重定向到一個普通文件
$ echo 'hello shiyanlou' > redirect
$ cat redirect
注意不要將管道和重定向混淆,管道默認是連接前一個命令的輸出到下一個命令的輸入,而重定向通常是需要一個文件來建立兩個命令的連接,仔細體會一下上述第三個操作和最後兩個操作的異同點。
重定向標准輸出到文件,這是一個很實用的操作,另一個很實用的操作是將標准錯誤重定向,標准輸出和標准錯誤都被指向偽終端的屏幕顯示,所以經常看到的一個命令的輸出通常是同時包含了標准輸出和標准錯誤的結果的。比如下面的操作:
# 使用cat 命令同時讀取兩個文件,其中一個存在,另一個不存在
$ cat Documents/test.c\~ hello.c
# 你可以看到除了正確輸出了前一個文件的內容,還在末尾出現了一條錯誤信息
# 下面我們將輸出重定向到一個文件,根據我們前面的經驗,這裡將在看不到任何輸出了
$ cat Documents/test.c\~ hello.c > somefile
遺憾的是,這裡依然出現了那條錯誤信息,這正是因為如我上面說的那樣,標准輸出和標准錯誤雖然都指向終端屏幕,實際它們並不一樣。那有的時候我們就是要可以隱藏某些錯誤或者警告,那又該怎麼做呢。這就需要用到前面講的文件描述符了:
# 將標准錯誤重定向到標准輸出,再將標准輸出重定向到文件,注意要將重定向到文件寫到前面
$ cat Documents/test.c\~ hello.c >somefile 2>&1
# 或者只用bash提供的特殊的重定向符號"&"將標准錯誤和標准輸出同時重定向到文件
$ cat Documents/test.c\~ hello.c &>somefilehell
注意你應該在輸出重定向文件描述符前加上&
,否則shell會當做重定向到一個文件名為1的文件中
tee
命令同時重定向到多個文件經常你可能還有這樣的需求,除了將需要將輸出重定向到文件之外也需要將信息打印在終端,那麼你可以使用tee
命令來實現:
$ echo 'hello shiyanlou' | tee hello
你應該可以看出我們前面的重定向操作都只是臨時性的,即只對當前命令有效,那如何做到“永久”有效呢,比如在一個腳本中,你需要某一部分的命令的輸出全部進行重定向,難道要讓你在每個命令上面加上臨時重定向的操作嘛,當然不需要,我們可以使用exec
命令實現“永久”重定向。exec
命令的作用是使用指定的命令替換當前的 Shell,及使用一個進程替換當前進程,或者指定新的重定向:
# 先開啟一個子 Shell
$ zsh
# 使用exec替換當前進程的重定向,將標准輸出重定向到一個文件
$ exec 1>somefile
# 後面你執行的命令的輸出都將被重定向到文件中,直到你退出當前子shell,或取消exec的重定向(後面將告訴你怎麼做)
$ ls
$ exit
$ cat somefile
默認在 Shell 中可以有9個打開的文件描述符,上面我們使用了也是它默認提供的0
,1
,2
號文件描述符,另外我們還可以使用3-8的文件描述符,只是它們默認沒有打開而已,你可以使用下面命令查看當前 Shell 進程中打開的文件描述符:
$ cd /dev/fd/;ls -Al
同樣使用exec
命令可以創建新的文件描述符:
$ zsh
$ exec 3>somefile
# 先進入目錄,再查看,否則你可能不能得到正確的結果,然後再回到上一次的目錄
$ cd /dev/fd/;ls -Al;cd -
# 注意下面的命令>與&之間不應該有空格,如果有空格則會出錯
$ echo "this is test" >&3
$ cat somefile
$ exit
如上面我們打開的3號文件描述符,可以使用如下操作將它關閉:
$ exec 3>&-
$ cd /dev/fd;ls -Al;cd -
在 Linux 中有一個被成為“黑洞”的設備文件,所以導入它的數據都將被“吞噬”。
在類 UNIX 系統中,/dev/null,或稱空設備,是一個特殊的設備文件,它通常被用於丟棄不需要的輸出流,或作為用於輸入流的空文件,這些操作通常由重定向完成。讀取它則會立即得到一個EOF。
我們可以利用設個/dev/null
屏蔽命令的輸出:
$ cat Documents/test.c\~ nefile 1>/dev/null 2>&1
向上面這樣的操作將使你得不到任何輸出結果。
xargs 是一條 UNIX 和類 UNIX 操作系統的常用命令。它的作用是將參數列表轉換成小塊分段傳遞給其他命令,以避免參數列表過長的問題。
這個命令在有些時候十分有用,特別是當用來處理產生大量輸出結果的命令如 find,locate 和 grep 的結果,詳細用法請參看 man 文檔。
$ cut -d: -f1 < /etc/passwd | sort | xargs echo
上面這個命令用於將/etc/passwd
文件按:
分割取第一個字段排序後,使用echo
命令生成一個列表。