鍍金池/ 教程/ Linux/ 大于小于號差別?
別人echo、你也echo,是問echo知多少?
何為 shell?
你要 if 還是 case 呢?
""(雙引號) 與''(單引號) 差在哪?
exec 跟 source 差在哪?
$(())與$()還有${}差在哪?
() 與 {} 差在哪?
shell and Carriage 關(guān)系
for what? while 與 until 差在哪?
大于小于號差別?
[^ ] 跟[! ]差在哪? (RE: Regular Expression)
[^ ] 跟[! ]差在哪? (wildcard)
特殊符號差異
&& 與 || 差在哪?
var=value 在 export 前后的差在哪?

大于小于號差別?

這次的題目,之前我在 CU 的 shell 版說明過了: (原帖的連接在論壇改版后,已經(jīng)失效) 這次我就不重寫了,將帖子的內(nèi)容 “抄” 下來就是了...

文件描述符 (fd, File Descriptor)

談到I/O redirection,不妨先讓我們認(rèn)識一下File Descriptor(fd,文件描述符)。

進(jìn)程的運(yùn)算,在大部分情況下,都是進(jìn)行數(shù)據(jù) (data) 的處理, 這些數(shù)據(jù)從哪里,讀進(jìn)來?又輸出到哪里呢? 這就是 file descriptor(fd) 的功用了。

在 shell 的進(jìn)程中,最常使用的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

Tips: linux 中的文件描述符 (fd) 用整數(shù)表示。 linux 中任何一個(gè)進(jìn)程都默認(rèn)打開三個(gè)文件, 這三個(gè)文件對應(yīng)的文件描述符分別是:0, 1, 2; 即 stdin, stdout, stderr.

我們可以用如下命令測試一下:

$ mail -s test root
this is a test mail。
please skip.
^d (同時(shí)按下ctrl 跟d鍵)

很明顯,mail進(jìn)程所讀進(jìn)的數(shù)據(jù),就是從 stdin 也就是 keyboard 讀進(jìn)的。 不過,不見得每個(gè)進(jìn)程的stdin都跟mail一樣 從keyboard讀進(jìn),因?yàn)檫M(jìn)程的作者可以從文件參數(shù)讀進(jìn)stdin, 如:

$ cat /etc/passwd

但,要是cat之后沒有文件參數(shù)則如何呢? 哦, 請你自己玩玩看...^_^

$ cat

Tips:

請留意數(shù)據(jù)輸出到哪里去了, 最后別忘了按ctrl+d(^d), 退出 stdin 輸入。

至于stdoutstderr,嗯... 等我有空再續(xù)吧...^_^ 還是,有哪位前輩來玩接龍呢?

相信,經(jīng)過上一個(gè)練習(xí)后, 你對stdinstdout應(yīng)該不難理解了吧? 然后,讓我們看看stderr好了。

事實(shí)上,stderr沒什么難理解的: 說白了就是 “錯(cuò)誤信息” 要往哪里輸出而已... 比方說, 若讀進(jìn)的文件參數(shù)不存在的, 那我們在 monitor 上就看到了:

$ ls no.such.file
ls: no.such.file: No such file or directory

若同一個(gè)命令,同時(shí)成生stdoutstderr呢? 那還不簡單,都送到 monitor 來就好了:

$ touch my.file
$ ls my.file on.such.file
ls: no.such.file: No such file or directory
my.file

okay, 至此,關(guān)于 fd 及其名稱、還有相關(guān)聯(lián)的設(shè)備, 相信你已經(jīng)沒問題了吧?

I/O 重定向 (I/O Redirection)

那好,接下來讓我們看看如何改變這些 fd 的預(yù)設(shè)數(shù)據(jù)通道。

  • <來改變讀進(jìn)的數(shù)據(jù)通道 (stdin),使之從指定的文件讀進(jìn)。
  • >來改變輸出的數(shù)據(jù)通道 (stdout,stderr), 使之輸出到指定的文件。

輸入重定向n<(input redirection)

比方說:

$ cat < my.file

就是從 my.file 讀入數(shù)據(jù)

$ mail -s test root < /etc/passwd

則是從 / etc/passwd 讀入...

這樣一來,stdin 將不再是從 keyboard 讀入, 而是從指定的文件讀入了...

嚴(yán)格來說,<符號之前需要指定一個(gè) fd 的 (之前不能有空白),但因?yàn)?0 是<的預(yù)設(shè)值,因此,<0<是一樣的 。

okay,這樣好理解了吧?

那要是用兩個(gè)<,即<<又是啥呢? 這是所謂的here document,它可以讓我們輸入一段文本,直到讀到<< 后指定的字符串。

比方說:

$ cat <<EOF
first line here
second line here
third line here
EOF

這樣的話,cat會讀入 3 個(gè)句子, 而無需從 keyboard 讀進(jìn)數(shù)據(jù)且要等到 (ctrl+d, ^d) 結(jié)束輸入。

重定向輸出>n(output redirection)

當(dāng)你搞懂了0< 原來就是改變stdin的數(shù)據(jù)輸入通道之后, 相信要理解如下兩個(gè) redirection 就不難了:

  • 1> #改變 stdout 的輸出通道;
  • 2> #改變 stderr 的輸出通道;

兩者都是將原來輸出到 monitor 的數(shù)據(jù), 重定向輸出到指定的文件了。

由于 1 是>的預(yù)設(shè)值, 因此,1>>是相同的,都是改變stdout。

用上次的 ls 的例子說明一下好了:

$ ls my.file no.such.file 1>file.out
ls: no.such.file: No such file or directory

這樣 monitor 的輸出就只剩下stderr的輸出了, 因?yàn)閟tdout重定向輸出到文件 file.out 去了。

$ ls my.file no.such.file 2>file.err
my.file

這樣 monitor 就只剩下了stdout, 因?yàn)閟tderr重定向輸出到文件 file.err 了。

$ ls my.file no.such.file 1>file.out 2>file.err

這樣 monitor 就啥也沒有了, 因?yàn)?code>stdout與stderr都重定向輸出到文件了。

呵呵,看來要理解>一點(diǎn)也不難啦是不? 沒騙你吧? ^_^ 不過有些地方還是要注意一下的。

$ ls my.file no.such.file 1>file.both 2>file.both

假如stdout(1) 與stderr(2) 都同時(shí)在寫入 file.both 的話, 則是采取 "覆蓋" 的方式:后來寫入覆蓋前面的。

讓我們假設(shè)一個(gè)stdoutstderr同時(shí)寫入到 file.out 的情形好了;

  • 首先stdout寫入 10 個(gè)字符
  • 然后stderr寫入 6 個(gè)字符

那么,這時(shí)原本的stdout輸出的 10 個(gè)字符, 將被stderr輸出的 6 個(gè)字符覆蓋掉了。

那如何解決呢?所謂山不轉(zhuǎn)路轉(zhuǎn),路不轉(zhuǎn)人轉(zhuǎn)嘛, 我們可以換一個(gè)思維: 將stderr導(dǎo)進(jìn)stdout 或者將stdout導(dǎo)進(jìn)到stderr, 而不是大家在搶同一份文件,不就行了。 bingo 就是這樣啦:

  • 2>&1 #將stderr并進(jìn)stdout輸出
  • 1>&2 或者 >&2 #將stdout并進(jìn)stderr輸出。

這樣,不就皆大歡喜了嗎? ~~~ ^_^

不過,光解決了同時(shí)寫入的問題還不夠, 我們還有其他技巧需要了解的。 故事還沒有結(jié)束,別走開廣告后,我們在回來....

I/O 重定向與 linux 中的/dev/null

okay,這次不講 I/O Redirection, 請佛吧... (有沒有搞錯(cuò)?網(wǎng)中人是否頭殼燒壞了?...) 嘻~~~^_^

學(xué)佛的最高境界,就是 "四大皆空"。 至于是空哪四大塊,我也不知,因?yàn)槲疫€沒有到那個(gè)境界.. 這個(gè) “空” 字, 卻非常值得反復(fù)把玩: --- 色即是空,空即是色 好了,施主要是能夠領(lǐng)會 "空" 的禪意,那離修成正果不遠(yuǎn)了。

在 linux 的文件系統(tǒng)中,有個(gè)設(shè)備文件: /dev/null。許多人都問過我,那是什么玩意兒? 我跟你說好了,那就是 "空" 啦。

沒錯(cuò)空空如也的空就是 null 了... 請問施主是否忽然有所頓悟了呢? 然則恭喜了。

這個(gè) null 在 I/O Redirection 中可有用的很呢?

  • 將 fd 1跟 fd 2重定向到 / dev/null 去,就可忽略 stdout, stderr 的輸出。
  • 將 fd 0重定向到 / dev/null,那就是讀進(jìn)空 (nothing)。

比方說,我們在執(zhí)行一個(gè)進(jìn)程時(shí),會同時(shí)輸出到 stdout 與 stderr, 假如你不想看到 stderr(也不想存到文件), 那就可以:

$ ls my.file no.such.file 2>/dev/null
my.file

若要相反:只想看到 stderr 呢? 還不簡單將 stdout,重定向的 / dev/null 就行:

$ ls my.file no.such.file >/dev/null
ls: no.such.file: No such file or directory

那接下來,假如單純的只跑進(jìn)程,而不想看到任何輸出呢? 哦,這里留了一手,上次沒講的法子, 專門贈(zèng)與有緣人... ^_^ 除了用 >/dev/null 2>&1之外,你還可以如此:

$ ls my.file no.such.file &>/dev/null```

>**Tips:**
>
>將 &> 換成 >& 也行!

### 重定向輸出 append (>>)

okay? 請完佛,接下來,再讓我們看看如下情況:

$ echo "1" > file.out $ cat file.out 1 $ echo "2" > file.out $ cat file.out 2


看來,我們在重定向 stdout 或 stderr 進(jìn)一個(gè)文件時(shí), 似乎永遠(yuǎn)只能獲得最后一次的重定向的結(jié)果. 那之前的內(nèi)容呢?

呵呵,要解決這個(gè)問題,很簡單啦,將`>`換成`>>` 就好了;

$ echo "3" >> file.out $ cat file.out 2 3


如此一來,被重定向的文件的之前的內(nèi)容并不會丟失, 而新的內(nèi)容則一直追加在最后面去。so easy?...

但是,只要你再次使用`>`來重定向輸出的話, 那么,原來文件的內(nèi)容被 truncated(清洗掉)。 這是,你要如何避免呢? ---- 備份, yes,我聽到了,不過,還有更好的嗎? 既然與施主這么有緣分,老衲就送你一個(gè)錦囊妙法吧:

$ set -o noclobber $ echo "4" > file.out -bash:file: cannot overwrite existing file.


那,要如何取消這個(gè)限制呢? 哦,將`set -o`換成 `set +o`就行了:

$ set +o noclobber $ echo "5" > file.out $ cat file.out 5


再問:那有辦法不取消而又 “臨時(shí)” 改寫目標(biāo)文件嗎? 哦,佛曰:不可告也。 啊,~ 開玩笑的,開玩笑啦~^_^, 哎,早就料到人心是不足的了

$ set -o noclobber $ echo "6" >| file.out $ cat file.out 6


留意到?jīng)]有: **在`>`后面加個(gè)`|`就好, 注意: `>`與`|`之間不能有空白哦**...

### I/O Redirection 的優(yōu)先級

呼....(深呼吸吐納一下吧)~~~ ^_^ 再來還有一個(gè)難題要你去參透呢:

$ echo "some text here" >file $ cat < file some text here $cat < file >file.bak $cat < file.bak some text here $cat < file >file


嗯?注意到?jīng)]有? --- 怎么最后那個(gè) cat 命令看到 file 是空的呢? why? why? why?

前面提到:`$cat < file > file`之后, 原本有內(nèi)容的文件,結(jié)果卻被清空了。 要理解這個(gè)現(xiàn)象其實(shí)不難, 這只是 priority 的問題而已: ** 在 IO Redirection 中, stdout 與 stderr 的管道先準(zhǔn)備好, 才會從 stdin 讀入數(shù)據(jù)。** 也就是說,在上例中,`>file`會將 file 清空, 然后才讀入 `< file`。 但這時(shí)候文件的內(nèi)容已被清空了,因此就變成了讀不進(jìn)任何數(shù)據(jù)。

哦,~ 原來如此~^_^ 那... 如下兩例又如何呢?

$ cat <> file $ cat < file >>file



嗯... 同學(xué)們,這兩個(gè)答案就當(dāng)練習(xí)題嘍, 下課前交作業(yè)。

>**Tips:** 我們了解到`>file`能夠快速把文件 file 清空; 或者使用`:>file`同樣可以清空文件,`:>file`與`>file`的功能: 若文件 file 存在,則將 file 清空; 否則,創(chuàng)建空文件 file (等效于`touch file`); 二者的差別在于`>file`的方式不一定在所有的 shell 的都可用。
>
`exec 5<>file; echo "abcd" >&5; cat <&5` 將 file 文件的輸入、輸出定向到文件描述符 5, 從而描述符 5 可以接管 file 的輸入輸出; 因此,`cat <>file`等價(jià)于`cat < file`。
>
>而`cat < file >>file`則使 file 內(nèi)容成幾何級數(shù)增長。

好了, I/O Redirection 也快講完了, sorry, 因?yàn)槲乙仓恢肋@么多而已啦~ 嘻~^_^ 不過,還有一樣?xùn)|東是一定要講的,各位觀眾 (請自行配樂~!#@$%): 就是 pipe line 也。

### 管道 (pipe line)

談到`pipe line`,我相信不少人都不會陌生: 我們在很多 command line 上??吹絗|`符號就是 pipe line 了。

不過,pipe line 究竟是什么東東呢? 別急別急... 先查一下英文字典,看看 pipe 是什么意思? 沒錯(cuò)他就是 “水管” 的意思... 那么,你能想象一下水管是怎樣一個(gè)根接一根的嗎? 又, 每根水管之間的 input 跟 output 又如何呢? 靈光一閃:原來 pipe line 的 I/O 跟水管的 I/O 是一模一樣的: **上一個(gè)命令的 stdout 接到下一個(gè)命令的 stdin 去了** 的確如此。不管在 command line 上使用了多少個(gè) pipe line, 前后兩個(gè) command 的 I/O 是彼此連接的 (恭喜:你終于開放了 ^_^)

不過... 然而... 但是... ...stderr 呢? 好問題不過也容易理解: 若水管漏水怎么辦? 也就是說:在 pipe line 之間, 前一個(gè)命令的 stderr 是不會接進(jìn)下一個(gè)命令的 stdin 的, 其輸出,若不用 2>file 的話,其輸出在 monitor 上來。 這點(diǎn)請你在 pipe line 運(yùn)用上務(wù)必要注意的。

那,或許你有會問: **有辦法將 stderr 也喂進(jìn)下一個(gè)命令的 stdin 嗎?** (貪得無厭的家伙),方法當(dāng)然是有的,而且,你早已學(xué)習(xí)過了。 提示一下就好:** 請問你如何將 stderr 合并進(jìn) stdout 一同輸出呢? 若你答不出來,下課后再來問我...(如果你臉皮足夠厚的話...)

或許,你仍意猶未盡,或許,你曾經(jīng)碰到過下面的問題: 在`cmd1 | cmd2 | cmd3 | ... `這段 pipe line 中如何將 cmd2 的輸出保存到一個(gè)文件呢?

若你寫成`cmd1 | cmd2 >file | cmd3`的話, 那你肯定會發(fā)現(xiàn)`cmd3`的 stdin 是空的,(當(dāng)然了,你都將 水管接到別的水池了) 聰明的你或許會如此解決:

`cmd1 | cmd2 >file; cmd3 < file`

是的,你可以這樣做,但最大的壞處是: file I/O 會變雙倍,在 command 執(zhí)行的整個(gè)過程中, file I/O 是最常見的最大效能殺手。 凡是有經(jīng)驗(yàn)的 shell 操作者,都會盡量避免或降低 file I/O 的頻度。

那上面問題還有更好的方法嗎? 有的,那就是`tee`命令了。 **所謂的`tee`命令是在不影響原本 I/O 的情況下, 將 stdout 賦值到一個(gè)文件中去**。 因此,上面的命令行,可以如此執(zhí)行:

`cmd1 | cmd2 | tee file | cmd3`

在預(yù)設(shè)上,`tee`會改寫目標(biāo)文件, 若你要改為追加內(nèi)容的話,那可用 - a 參數(shù)選項(xiàng)。

基本上,pipe line 的應(yīng)用在 shell 操作上是非常廣泛的。 尤其是在 text filtering 方面, 如,cat, more, head, tail, wc, expand, tr, grep, sed, awk... 等等文字處理工具。 搭配起 pipe line 來使用,你會覺得 command line 原來活得如此精彩的。 常讓人有 “眾里尋他千百度,驀然回首,那人卻在燈火闌珊處” 之感...

好了,關(guān)于 I/O Redirection 的介紹就到此告一段落。 若日后,有空的話,在為大家介紹其他在 shell 上好玩的東西。