資料流重定向
實驗介紹
你可能對重定向這個概念感到些許陌生,但你應該在前面的課程中多次見過
>
或
>>
操作了,并知道他們分别是将标準輸出導向一個檔案或追加到一個檔案中。這其實就是重定向,将原本輸出到标準輸出的資料重定向到一個檔案中,因為标準輸出(
/dev/stdout
)本身也是一個檔案,我們将指令輸出導向另一個檔案自然也是沒有任何問題的。
一、資料流重定向
下面我們簡單的回顧一下我們前面經常用到的兩個重定向操作:
$ echo 'hello shiyanlou' > redirect
$ echo 'www.shiyanlou.com' >> redirect
$ cat redirect
當然前面沒有用到的和
<
操作也是沒有問題的,如你了解的一樣,它們的差別在于重定向的方向不一緻而已,
<<
表示是從左到右,
>
右到左。
<
1.簡單的重定向
在更多了解 Linux 的重定向之前,我們需要先知道一些基本的東西,前面我們已經提到過 Linux 預設提供了三個特殊裝置,用于終端的顯示和輸出,分别為
stdin
(标準輸入,對應于你在終端的輸入),
stdout
(标準輸出,對應于終端的輸出),
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
初學者這裡要注意不要将管道和重定向混淆,管道預設是連接配接前一個指令的輸出到下一個指令的輸入,而重定向通常是需要一個檔案來建立兩個指令的連接配接,你可以仔細體會一下上述第三個操作和最後兩個操作的異同點。
2.标準錯誤重定向
重定向标準輸出到檔案,這是一個很實用的操作,另一個很實用的操作是将标準錯誤重定向,标準輸出和标準錯誤都被指向僞終端的螢幕顯示,是以我們經常看到的一個指令的輸出通常是同時包含了标準輸出和标準錯誤的結果的。比如下面的操作:
# 使用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的檔案中
3.使用 tee
指令同時重定向到多個檔案
tee
經常你可能還有這樣的需求,除了将需要将輸出重定向到檔案之外也需要将資訊列印在終端,那麼你可以使用
tee
指令來實作:
$ echo 'hello shiyanlou' | tee hello
4.永久重定向
你應該可以看出我們前面的重定向操作都隻是臨時性的,即隻對目前指令有效,那如何做到“永久”有效呢,比如在一個腳本中,你需要某一部分的指令的輸出全部進行重定向,難道要讓你在每個指令上面加上臨時重定向的操作嘛,當然不需要,我們可以使用
exec
指令實作“永久”重定向。
exec
指令的作用是使用指定的指令替換目前的 Shell,及使用一個程序替換目前程序,或者指定新的重定向:
# 先開啟一個子 Shell
$ zsh
# 使用exec替換目前程序的重定向,将标準輸出重定向到一個檔案
$ exec 1>somefile
# 後面你執行的指令的輸出都将被重定向到檔案中,直到你退出目前子shell,或取消exec的重定向(後面将告訴你怎麼做)
$ ls
$ exit
$ cat somefile
5.建立輸出檔案描述符
預設在 Shell 中可以有9個打開的檔案描述符,上面我們使用了也是它預設提供的
,
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
6.關閉檔案描述符
如上面我們打開的3号檔案描述符,可以使用如下操作将它關閉:
$ exec 3>&-
$ cd /dev/fd;ls -Al;cd -
7.完全屏蔽指令的輸出
在 Linux 中有一個被成為“黑洞”的裝置檔案,是以導入它的資料都将被“吞噬”。
在類 UNIX 系統中,/dev/null,或稱空裝置,是一個特殊的裝置檔案,它通常被用于丢棄不需要的輸出流,或作為用于輸入流的空檔案,這些操作通常由重定向完成。讀取它則會立即得到一個EOF。
我們可以利用設個
/dev/null
屏蔽指令的輸出:
$ cat Documents/test.c\~ nefile 1>/dev/null 2>&1
向上面這樣的操作将使你得不到任何輸出結果。
8.使用 xargs 分割參數清單
xargs 是一條 UNIX 和類 UNIX 作業系統的常用指令。它的作用是将參數清單轉換成小塊分段傳遞給其他指令,以避免參數清單過長的問題。
這個指令在有些時候十分有用,特别是當用來處理産生大量輸出結果的指令如 find,locate 和 grep 的結果,詳細用法請參看 man 文檔。
$ cut -d: -f1 < /etc/passwd | sort | xargs echo
上面這個指令用于将
/etc/passwd
檔案按
:
分割取第一個字段排序後,使用
echo
指令生成一個清單。
作業
1、了解下面這段代碼的的作用,實際這段代碼不會正常工作,請結合這一小節的知識分析這段代碼沒有正确工作的原因,并設法解決這個問題。
如果你還沒有 Shell 腳本程式設計的基礎,你可以選擇跳過或者到這裡進階 Bash 程式設計指南學習
while read filename; do
rm -iv $filename
done <<(ls)
2、之前介紹過一個在指令行将圖檔轉換為 ascii 字元檢視的工具 aview/asciiview,不過它是黑白的。現在,這裡是個彩色的:
$ sudo apt-get install caca-utils
$ cacaview <pic_file>
$ cacademo
$ cacafire