- 論壇徽章:
- 1
|
shell 十三問(wèn)?
11) > 與 < 差在哪?
這次的題目之前我在 CU 的 shell 版已說(shuō)明過(guò)了:
(原貼連結(jié)在論壇改版後已經(jīng)失效)
這次我就不重寫了,將貼子的內(nèi)容"抄"下來(lái)就是了...
--------------
11.1
談到 I/O redirection ,不妨先讓我們認(rèn)識(shí)一下 File Descriptor (FD) 。
程式的運(yùn)算,在大部份情況下都是進(jìn)行數(shù)據(jù)(data)的處理,
這些數(shù)據(jù)從哪讀進(jìn)?又,送出到哪裡呢?
這就是 file descriptor (FD) 的功用了。
在 shell 程式中,最常使用的 FD 大概有三個(gè),分別為:
0: Standard Input (STDIN)
1: Standard Output (STDOUT)
2: Standard Error Output (STDERR)
在標(biāo)準(zhǔn)情況下,這些 FD 分別跟如下設(shè)備(device)關(guān)聯(lián):
stdin(0): keyboard
stdout(1): monitor
stderr(2): monitor
我們可以用如下下命令測(cè)試一下:
- $ mail -s test root
- this is a test mail.
- please skip.
- ^d (同時(shí)按 crtl 跟 d 鍵)
復(fù)制代碼
很明顯,mail 程式所讀進(jìn)的數(shù)據(jù),就是從 stdin 也就是 keyboard 讀進(jìn)的。
不過(guò),不見(jiàn)得每個(gè)程式的 stdin 都跟 mail 一樣從 keyboard 讀進(jìn),
因?yàn)槌淌阶髡呖梢詮臋n案參數(shù)讀進(jìn) stdin ,如:
但,要是 cat 之後沒(méi)有檔案參數(shù)則又如何呢?
哦,請(qǐng)您自己玩玩看囉.... ^_^
(請(qǐng)留意數(shù)據(jù)輸出到哪裡去了,最後別忘了按 ^d 離開...)
至於 stdout 與 stderr ,嗯... 等我有空再續(xù)吧... ^_^
還是,有哪位前輩要來(lái)玩接龍呢?
--------------
11.2
沿文再續(xù),書接上一回... ^_^
相信,經(jīng)過(guò)上一個(gè)練習(xí)後,你對(duì) stdin 與 stdout 應(yīng)該不難理解吧?
然後,讓我們繼續(xù)看 stderr 好了。
事實(shí)上,stderr 沒(méi)甚麼難理解的:說(shuō)穿了就是"錯(cuò)誤信息"要往哪邊送而已...
比方說(shuō),若讀進(jìn)的檔案參數(shù)是不存在的,那我們?cè)?monitor 上就看到了:
- $ ls no.such.file
- ls: no.such.file: No such file or directory
復(fù)制代碼
若,一個(gè)命令同時(shí)產(chǎn)生 stdout 與 stderr 呢?
那還不簡(jiǎn)單,都送到 monitor 來(lái)就好了:
- $ touch my.file
- $ ls my.file no.such.file
- ls: no.such.file: No such file or directory
- my.file
復(fù)制代碼
okay,至此,關(guān)於 FD 及其名稱、還有相關(guān)聯(lián)的設(shè)備,相信你已經(jīng)沒(méi)問(wèn)題了吧?
那好,接下來(lái)讓我們看看如何改變這些 FD 的預(yù)設(shè)數(shù)據(jù)通道,
我們可用 < 來(lái)改變讀進(jìn)的數(shù)據(jù)通道(stdin),使之從指定的檔案讀進(jìn)。
我們可用 > 來(lái)改變送出的數(shù)據(jù)通道(stdout, stderr),使之輸出到指定的檔案。
比方說(shuō):
就是從 my.file 讀進(jìn)數(shù)據(jù)
- $ mail -s test root < /etc/passwd
復(fù)制代碼
則是從 /etc/passwd 讀進(jìn)...
這樣一來(lái),stdin 將不再是從 keyboard 讀進(jìn),而是從檔案讀進(jìn)了...
嚴(yán)格來(lái)說(shuō),< 符號(hào)之前需要指定一個(gè) FD 的(之間不能有空白),
但因?yàn)?0 是 < 的預(yù)設(shè)值,因此 < 與 0< 是一樣的﹗
okay,這個(gè)好理解吧?
那,要是用兩個(gè) << 又是啥呢?
這是所謂的 HERE Document ,它可以讓我們輸入一段文本,直到讀到 << 後指定的字串。
比方說(shuō):
- $ cat <<FINISH
- first line here
- second line there
- third line nowhere
- FINISH
復(fù)制代碼
這樣的話,cat 會(huì)讀進(jìn) 3 行句子,而無(wú)需從 keyboard 讀進(jìn)數(shù)據(jù)且要等 ^d 結(jié)束輸入。
至於 > 又如何呢?
且聽下回分解....
--------------
11.3
okay,又到講古時(shí)間~~~
當(dāng)你搞懂了 0< 原來(lái)就是改變 stdin 的數(shù)據(jù)輸入通道之後,相信要理解如下兩個(gè) redirection 就不難了:
* 1>
* 2>
前者是改變 stdout 的數(shù)據(jù)輸出通道,後者是改變 stderr 的數(shù)據(jù)輸出通道。
兩者都是將原本要送出到 monitor 的數(shù)據(jù)轉(zhuǎn)向輸出到指定檔案去。
由於 1 是 > 的預(yù)設(shè)值,因此,1> 與 > 是相同的,都是改變 stdout 。
用上次的 ls 例子來(lái)說(shuō)明一下好了:
- $ ls my.file no.such.file 1>file.out
- ls: no.such.file: No such file or directory
復(fù)制代碼
這樣 monitor 就只剩下 stderr 而已。因?yàn)?stdout 給寫進(jìn) file.out 去了。
- $ ls my.file no.such.file 2>file.err
- my.file
復(fù)制代碼
這樣 monitor 就只剩下 stdout ,因?yàn)?stderr 寫進(jìn)了 file.err 。
- $ ls my.file no.such.file 1>file.out 2>file.err
復(fù)制代碼
這樣 monitor 就啥也沒(méi)有,因?yàn)?stdout 與 stderr 都給轉(zhuǎn)到檔案去了...
呵~~~ 看來(lái)要理解 > 一點(diǎn)也不難啦﹗是不?沒(méi)騙你吧? ^_^
不過(guò),有些地方還是要注意一下的。
首先,是同時(shí)寫入的問(wèn)題。比方如下這個(gè)例子:
- $ ls my.file no.such.file 1>file.both 2>file.both
復(fù)制代碼
假如 stdout(1) 與 stderr(2) 都同時(shí)在寫入 file.both 的話,
則是採(cǎi)取“覆蓋”方式:後來(lái)寫入的覆蓋前面的。
讓我們假設(shè)一個(gè) stdout 與 stderr 同時(shí)寫入 file.out 的情形好了:
* 首先 stdout 寫入10個(gè)字元
* 然後 stderr 寫入 6 個(gè)字元
那麼,這時(shí)候原本 stdout 輸出的 10 個(gè)字元就被 stderr 覆蓋掉了。
那,如何解決呢?所謂山不轉(zhuǎn)路轉(zhuǎn)、路不轉(zhuǎn)人轉(zhuǎn)嘛,
我們可以換一個(gè)思維:將 stderr 導(dǎo)進(jìn) stdout 或?qū)?stdout 導(dǎo)進(jìn) sterr ,而不是大家在搶同一份檔案,不就行了﹗
bingo﹗就是這樣啦:
* 2>&1 就是將 stderr 併進(jìn) stdout 作輸出
* 1>&2 或 >&2 就是將 stdout 併進(jìn) stderr 作輸出
於是,前面的錯(cuò)誤操作可以改為:
- $ ls my.file no.such.file 1>file.both 2>&1
- 或
- $ ls my.file no.such.file 2>file.both >&2
復(fù)制代碼
這樣,不就皆大歡喜了嗎? 呵~~~ ^_^
不過(guò),光解決了同時(shí)寫入的問(wèn)題還不夠,我們還有其他技巧需要了解的。
故事還沒(méi)結(jié)束,別走開﹗廣告後,我們?cè)倩貋?lái)...﹗
--------------
11.4
okay,這次不講 I/O Redirction ,講佛吧...
(有沒(méi)搞錯(cuò)?﹗網(wǎng)中人是否頭殼燒壞了?...) 嘻~~~ ^_^
學(xué)佛的最高境界,就是"四大皆空"。至於是空哪四大塊?我也不知,因?yàn)槲疫沒(méi)到那境界...
但這個(gè)"空"字,卻非常值得我們返複把玩的:
--- 色即是空、空即是色﹗
好了,施主要是能夠領(lǐng)會(huì)"空"的禪意,那離修成正果不遠(yuǎn)矣~~~
在 Linux 檔案系統(tǒng)裡,有個(gè)設(shè)備檔位於 /dev/null 。
許多人都問(wèn)過(guò)我那是甚麼玩意兒?我跟你說(shuō)好了:那就是"空"啦﹗
沒(méi)錯(cuò)﹗空空如也的空就是 null 了.... 請(qǐng)問(wèn)施主是否忽然有所頓誤了呢?然則恭喜了~~~ ^_^
這個(gè) null 在 I/O Redirection 中可有用得很呢:
* 若將 FD1 跟 FD2 轉(zhuǎn)到 /dev/null 去,就可將 stdout 與 stderr 弄不見(jiàn)掉。
* 若將 FD0 接到 /dev/null 來(lái),那就是讀進(jìn) nothing 。
比方說(shuō),當(dāng)我們?cè)趫?zhí)行一個(gè)程式時(shí),畫面會(huì)同時(shí)送出 stdout 跟 stderr ,
假如你不想看到 stderr (也不想存到檔案去),那可以:
- $ ls my.file no.such.file 2>/dev/null
- my.file
復(fù)制代碼
若要相反:只想看到 stderr 呢?還不簡(jiǎn)單﹗將 stdout 弄到 null 就行:
- $ ls my.file no.such.file >/dev/null
- ls: no.such.file: No such file or directory
復(fù)制代碼
那接下來(lái),假如單純只跑程式,不想看到任何輸出結(jié)果呢?
哦,這裡留了一手上次節(jié)目沒(méi)講的法子,專門贈(zèng)予有緣人﹗... ^_^
除了用 >/dev/null 2>&1 之外,你還可以如此:
- $ ls my.file no.such.file &>/dev/null
復(fù)制代碼
(提示:將 &> 換成 >& 也行啦~~! )
okay?講完佛,接下來(lái),再讓我們看看如下情況:
- $ echo "1" > file.out
- $ cat file.out
- 1
- $ echo "2" > file.out
- $ cat file.out
- 2
復(fù)制代碼
看來(lái),我們?cè)谥貙?dǎo) stdout 或 stderr 進(jìn)一份檔案時(shí),似乎永遠(yuǎn)只獲得最後一次導(dǎo)入的結(jié)果。
那,之前的內(nèi)容呢?
呵~~~ 要解決這個(gè)問(wèn)提很簡(jiǎn)單啦,將 > 換成 >> 就好:
- $ echo "3" >> file.out
- $ cat file.out
- 2
- 3
復(fù)制代碼
如此一來(lái),被重導(dǎo)的目標(biāo)檔案之內(nèi)容並不會(huì)失去,而新的內(nèi)容則一直增加在最後面去。
easy ? 呵 ... ^_^
但,只要你再一次用回單一的 > 來(lái)重導(dǎo)的話,那麼,舊的內(nèi)容還是會(huì)被"洗"掉的﹗
這時(shí),你要如何避免呢?
----備份﹗ yes ,我聽到了﹗不過(guò).... 還有更好的嗎?
既然與施主這麼有緣份,老納就送你一個(gè)錦囊妙法吧:
- $ set -o noclobber
- $ echo "4" > file.out
- -bash: file: cannot overwrite existing file
復(fù)制代碼
那,要如何取消這個(gè)"限制"呢?
哦,將 set -o 換成 set +o 就行:
- $ set +o noclobber
- $ echo "5" > file.out
- $ cat file.out
- 5
復(fù)制代碼
再問(wèn):那... 有辦法不取消而又"臨時(shí)"蓋寫目標(biāo)檔案嗎?
哦,佛曰:不可告也﹗
啊~~~ 開玩笑的、開玩笑的啦~~~ ^_^ 唉,早就料到人心是不足的了﹗
- $ set -o noclobber
- $ echo "6" >| file.out
- $ cat file.out
- 6
復(fù)制代碼
留意到?jīng)]有:在 > 後面再加個(gè)" | "就好(注意: > 與 | 之間不能有空白哦)....
呼.... (深呼吸吐納一下吧)~~~ ^_^
再來(lái)還有一個(gè)難題要你去參透的呢:
- $ echo "some text here" > file
- $ cat < file
- some text here
- $ cat < file > file.bak
- $ cat < file.bak
- some text here
- $ cat < file > file
- $ cat < file
復(fù)制代碼
嗯?﹗注意到?jīng)]有?﹗﹗
---- 怎麼最後那個(gè) cat 命令看到的 file 竟是空的?﹗
why? why? why?
同學(xué)們:下節(jié)課不要遲到囉~~~!
--------------
11.5
噹噹噹~~~ 上課囉~~~ ^_^
前面提到:$ cat < file > file 之後原本有內(nèi)容的檔案結(jié)果卻被洗掉了﹗
要理解這一現(xiàn)像其實(shí)不難,這只是 priority 的問(wèn)題而已:
* 在 IO Redirection 中,stdout 與 stderr 的管道會(huì)先準(zhǔn)備好,才會(huì)從 stdin 讀進(jìn)資料。
也就是說(shuō),在上例中,> file 會(huì)先將 file 清空,然後才讀進(jìn) < file ,
但這時(shí)候檔案已經(jīng)被清空了,因此就變成讀不進(jìn)任何資料了...
哦~~~ 原來(lái)如此~~~~ ^_^
那... 如下兩例又如何呢?
- $ cat <> file
- $ cat < file >> file
復(fù)制代碼
嗯... 同學(xué)們,這兩個(gè)答案就當(dāng)練習(xí)題囉,下節(jié)課之前請(qǐng)交作業(yè)﹗
好了,I/O Redirection 也快講完了,sorry,因?yàn)槲乙仓恢肋@麼多而已啦~~~ 嘻~~ ^_^
不過(guò),還有一樣?xùn)|東是一定要講的,各位觀眾(請(qǐng)自行配樂(lè)~!#@!$%) :
---- 就是 pipe line 也﹗
談到 pipe line ,我相信不少人都不會(huì)陌生:
我們?cè)诤芏?command line 上?吹降" | "符號(hào)就是 pipe line 了。
不過(guò),究竟 pipe line 是甚麼東東呢?
別急別急... 先查一下英漢字典,看看 pipe 是甚麼意思?
沒(méi)錯(cuò)﹗它就是"水管"的意思...
那麼,你能想像一下水管是怎麼一根接著一根的嗎?
又,每根水管之間的 input 跟 output 又如何呢?
嗯??
靈光一閃:原來(lái) pipe line 的 I/O 跟水管的 I/O 是一模一樣的:
* 上一個(gè)命令的 stdout 接到下一個(gè)命令的 stdin 去了﹗
的確如此... 不管在 command line 上你使用了多少個(gè) pipe line ,
前後兩個(gè) command 的 I/O 都是彼此連接的﹗(恭喜:你終於開竅了﹗ ^_^ )
不過(guò)... 然而... 但是... ... stderr 呢?
好問(wèn)題﹗不過(guò)也容易理解:
* 若水管漏水怎麼辦?
也就是說(shuō):在 pipe line 之間,前一個(gè)命令的 stderr 是不會(huì)接進(jìn)下一命令的 stdin 的,
其輸出,若不用 2> 導(dǎo)到 file 去的話,它還是送到監(jiān)視器上面來(lái)﹗
這點(diǎn)請(qǐng)你在 pipe line 運(yùn)用上務(wù)必要注意的。
那,或許你又會(huì)問(wèn):
* 有辦法將 stderr 也餵進(jìn)下一個(gè)命令的 stdin 去嗎?
(貪得無(wú)厭的家夥﹗)
方法當(dāng)然是有,而且你早已學(xué)過(guò)了﹗ ^_^
我提示一下就好:
* 請(qǐng)問(wèn)你如何將 stderr 合併進(jìn) stdout 一同輸出呢?
若你答不出來(lái),下課之後再來(lái)問(wèn)我吧... (如果你臉皮真夠厚的話...)
或許,你仍意尤未盡﹗或許,你曾經(jīng)碰到過(guò)下面的問(wèn)題:
* 在 cm1 | cm2 | cm3 ... 這段 pipe line 中,若要將 cm2 的結(jié)果存到某一檔案呢?
若你寫成 cm1 | cm2 > file | cm3 的話,
那你肯定會(huì)發(fā)現(xiàn) cm3 的 stdin 是空的﹗(當(dāng)然啦,你都將水管接到別的水池了﹗)
聰明的你或許會(huì)如此解決:
- cm1 | cm2 > file ; cm3 < file
復(fù)制代碼
是的,你的確可以這樣做,但最大的壞處是:這樣一來(lái),file I/O 會(huì)變雙倍﹗
在 command 執(zhí)行的整個(gè)過(guò)程中,file I/O 是最常見(jiàn)的最大效能殺手。
凡是有經(jīng)驗(yàn)的 shell 操作者,都會(huì)盡量避免或降低 file I/O 的頻率。
那,上面問(wèn)題還有更好方法嗎?
有的,那就是 tee 命令了。
* 所謂 tee 命令是在不影響原本 I/O 的情況下,將 stdout 複製一份到檔案去。
因此,上面的命令行可以如此打:
- cm1 | cm2 | tee file | cm3
復(fù)制代碼
在預(yù)設(shè)上,tee 會(huì)改寫目標(biāo)檔案,若你要改為增加內(nèi)容的話,那可用 -a 參數(shù)達(dá)成。
基本上,pipe line 的應(yīng)用在 shell 操作上是非常廣泛的,尤其是在 text filtering 方面,
凡舉 cat, more, head, tail, wc, expand, tr, grep, sed, awk, ... 等等文字處理工具,
搭配起 pipe line 來(lái)使用,你會(huì)驚覺(jué) command line 原來(lái)是活得如此精彩的﹗
常讓人有"眾裡尋他千百度,驀然回首,那人卻在燈火闌珊處﹗"之感... ^_^
....
好了,關(guān)於 I/O Redirection 的介紹就到此告一段落。
若日後有空的話,再為大家介紹其它在 shell 上好玩的東西﹗bye... ^_^
[ 本帖最后由 網(wǎng)中人 于 2008-10-30 02:24 編輯 ] |
|