分类: LINUX
2008-09-03 09:54:19
5.标准输入、输出和错误
当我们在s h e l l中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。由于文件描述符不容易记忆, s h e l l同时也给出了相应的文件名。 下面就是这些文件描述符及它们通常所对应的文件名: 文件文件描述符 输入文件—标准输入stdin 0:它是命令的输入,缺省是键盘,也可以是文件或其他命令的输出。 输出文件—标准输出stdout 1:它是命令的输出,缺省是屏幕,也可以是文件。 错误输出文件—标准错误stderr 2:这是命令错误的输出,缺省是屏幕,同样也可以是文件。 如果没有特别指定文件说明符,命令将使用缺省的文件说明符(你的屏幕,更确切地说是你的终端)。 系统中实际上有1 2个文件描述符,但是正如我们在上表中所看到的, 0、1、2是标准输入、输出和错误。可以任意使用文件描述符3到9。 在执行命令时,可以指定命令的标准输入、输出和错误,要实现这一点就需要使用文件重定向。下面列出了最常用的重定向组合,并给出了相应的文件描述符。 在对标准错误进行重定向时,必须要使用文件描述符,但是对于标准输入和输出来说,这不是必需的。 常用文件重定向命令 command > filename 把标准输出重定向到一个新文件中 command >> filename 把标准输出重定向到一个文件中(追加) command 1 > fielname 把标准输出重定向到一个文件中 command > filename 2>&1 把标准输出和标准错误一起重定向到一个文件中 command 2> filename 把标准错误重定向到一个文件中 command 2>> filename 把标准输出重定向到一个文件中(追加) command >> filename 2>&1 把标准输出和标准错误一起重定向到一个文件中(追加) command < filename >filename2 把c o m m a n d命令以f i l e n a m e文件作为标准输入,以f i l e n a m e 2文件作为标准输出 command < filename 把c o m m a n d命令以f i l e n a m e文件作为标准输入 command << delimiter 把从标准输入中读入,直至遇到d e l i m i t e r分界符 command <&m 把文件描述符m作为标准输入 command >&m 把标准输出重定向到文件描述符m中 command <&- 关闭标准输入 补充 File Descriptor (FD) 程式的運算,在大部份情況下都是進行數據(data)的處理, 這些數據?哪淖x進?又,送出到哪裡呢? 這就是 file descriptor (FD) 的功用了。 在 shell 程式中,最常使用的 FD 大概有三個,分別為: 0: Standard Input (STDIN) 1: Standard Output (STDOUT) 2: Standard Error Output (STDERR) 在標準情況下,這些 FD 分別跟如下設備(device)關聯: stdin(0): keyboard stdout(1): monitor stderr(2): monitor 我們可以用如下下命令測試一下: 代码: $ mail -s test root this is a test mail. please skip. ^d (同時按 crtl 跟 d 鍵) 不過,不見得每個程式的 stdin 都跟 mail 一樣? keyboard 讀進, 因為程式作者可以?臋n案參數讀進 stdin ,如: 代码: $ cat /etc/passwd 哦,請您自己玩玩看囉.... ^_^ 代码: $ cat 事?上,stderr 沒甚麼難理解的:說穿了就是"錯誤信息"要往哪邊送而已... 比方說,若讀進的檔案參數是不存在的,那我們在 monitor 上就看到了: 代码: $ ls no.such.file ls: no.such.file: No such file or directory 那還不簡單,都送到 monitor ?砭秃昧耍篬code] $ touch my.file $ ls my.file no.such.file ls: no.such.file: No such file or directory my.file [/code] okay,至此,關於 FD 及其名稱、還有相關聯的設備,相信你已經沒問題了吧? 那好,接下?碜屛覀兛纯慈绾胃淖冞@些 FD 的預設數據通道, 我們可用 < ?砀淖冏x進的數據通道(stdin),使之?闹付ǖ臋n案讀進。 我們可用 > ?砀淖兯统龅臄祿通道(stdout, stderr),使之輸出到指定的檔案。 比方說: 代码: $ cat < my.file 代码: $ mail -s test root < /etc/passwd 這樣一?恚瑂tdin 將不再是? keyboard 讀進,而是?臋n案讀進了... 嚴格?碚f,< 符號之前需要指定一個 FD 的(之間不能有空白), 但因為 0 是 < 的預設值,因此 < 與 0< 是一樣的﹗ 那,要是用兩個 << 又是啥呢? 這是所謂的 HERE Document ,它可以讓我們輸入一段文本,直到讀到 << 後指定的字串。 比方說: 代码: $ cat < second line there third line nowhere FINISH 當你搞懂了 0< 原?砭褪歉淖 stdin 的數據輸入通道之後,相信要理解如下兩個 redirection 就不難了: * 1> * 2> 前者是改變 stdout 的數據輸出通道,後者是改變 stderr 的數據輸出通道。 兩者都是將原本要送出到 monitor 的數據轉向輸出到指定檔案去。 由於 1 是 > 的預設值,因此,1> 與 > 是相同的,都是改 stdout 。 用上面的 ls 例子?碚f明一下好了: 代码: $ ls my.file no.such.file 1>file.out ls: no.such.file: No such file or directory 代码: $ ls my.file no.such.file 2>file.err my.file 代码: $ ls my.file no.such.file 1>file.out 2>file.err 不過,有些地方還是要注意一下. 首先,是 file locking 的問題。比方如下這個例子: 代码: $ ls my.file no.such.file 1>file.both 2>file.both 假如 stdout(1) 與 stderr(2) 都同時在寫入 file.both 的話, 則要看它們在寫入時否碰到同時競爭的情形了,基本上是"先搶先贏"的原則。 讓我們用"慢鏡頭"?砜匆幌 stdout 與 stderr 同時寫入 file.out 的情形好了: * 第 1, 2, 3 秒為 stdout 寫入 * 第 3, 4, 5 秒為 stderr 寫入 那麼,這時候 stderr 的第 3 秒所寫的數據就?G失掉了﹗ 要是我們能控制 stderr 必須等 stdout 寫完再寫,或倒過?恚瑂tdout 等 stderr 寫完再寫,那問題就能解決。 但?募夹g上,較難掌控的,尤其是 FD 在作"長期性"的寫入時... 那,如何解決呢?所謂山不轉路轉、路不轉人轉嘛, 我們可以換一個思維:將 stderr 導進 stdout 或將 stdout 導進 sterr ,而不是大家在搶同一份檔案,不就行了﹗ bingo﹗就是這樣啦: * 2>&1 就是將 stderr ?氵M stdout 作輸出 * 1>&2 或 >&2 就是將 stdout ?氵M stderr 作輸出 於是,前面的錯誤操作可以改為: 代码: $ ls my.file no.such.file 1>file.both 2>&1 代码: $ ls my.file no.such.file 2>file.both >&2 許多人都問過我那是甚麼玩意兒?那就是"空"啦﹗ 沒錯﹗空空如也的空就是 null 了.... 這個 null 在 I/O Redirection 中可有用得很呢: * 若將 FD1 跟 FD2 轉到 /dev/null 去,就可將 stdout 與 stderr 弄不見掉。 * 若將 FD0 接到 /dev/null ?恚蔷褪亲x進 nothing 。 比方說,當我們在執行一個程式時,畫面會同時送出 stdout 跟 stderr , 假如你不想看到 stderr (也不想存到檔案去),那可以: 代码: $ ls my.file no.such.file 2>/dev/null my.file 代码: $ ls my.file no.such.file >/dev/null ls: no.such.file: No such file or directory 除了用 >/dev/null 2>&1 之外,你還可以如此: 代码: $ ls my.file no.such.file &>/dev/null 另外,看下面: 代码: $ echo "1" > file.out $ cat file.out 1 $ echo "2" > file.out $ cat file.out 2 那,之前的內容呢? 呵~~~ 要解決這個問提很簡單啦,將 > 換成 >> 就好: $ echo "3" >> file.out $ cat file.out 2 3 如此一?恚恢貙У哪繕藱n案之內容?K不會失去,而新的內容則一直增加在最後面去。 但,只要你再一次用回單一的 > ?碇貙У脑挘屈N,舊的內容還是會被"洗"掉的﹗ 這時,你要如何避免呢? ----備份﹗ yes ,我聽到了﹗不過.... 還有更好的嗎? 代码: $ set -o noclobber $ echo "4" > file.out -bash: file: cannot overwrite existing file 哦,將 set -o 換成 set +o 就行: 代码: $ set +o noclobber $ echo "5" > file.out $ cat file.out 5 代码: $ set -o noclobber $ echo "6" >| file.out $ cat file.out 6 再?磉有一個難題要你去參透的呢: 代码: $ 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 ---- 怎麼最後那個 cat 命令看到的 file 竟是空的?﹗ why? why? why? 要理解這一現像其?不難,這只是 priority 的問題而已: * 在 IO Redirection 中,stdout 與 stderr 的管道會先準備好,才會? stdin 讀進資料。 也就是說,在上例中,> file 會先將 file 清空,然後才讀進 < file , 但這時候檔案已經被清空了,因此就變成讀不進任何資料了... 哦~~~ 原?砣绱藒~~~ ^_^ 那... 如下兩例又如何呢? 代码: $ cat <> file $ cat < file >> file 談到 pipe line ,我相信不少人都不會陌生: 我們在很多 command line 上常看到的" | "符號就是 pipe line 了。 不過,究竟 pipe line 是甚麼東東呢? 別急別急... 先查一下英?h字典,看看 pipe 是甚麼意思? 沒錯﹗它就是"水管"的意思... 那麼,你能想像一下水管是怎麼一根接著一根的嗎? 又,每根水管之間的 input 跟 output 又如何呢? 嗯?? 靈光一閃:原? pipe line 的 I/O 跟水管的 I/O 是一模一樣的: * 上一個命令的 stdout 接到下一個命令的 stdin 去了﹗ 的確如此... 不管在 command line 上你使用了多少個 pipe line , 前後兩個 command 的 I/O 都是彼此連接的﹗(恭喜:你終於開竅了﹗ ^_^ ) 不過... 然而... 但是... ... stderr 呢? 好問題﹗不過也容易理解: * 若水管漏水怎麼辦? 也就是說:在 pipe line 之間,前一個命令的 stderr 是不會接進下一命令的 stdin 的, 其輸出,若不用 2> 導到 file 去的話,它還是送到監視器上面?愆u 這點請你在 pipe line 運用上務必要注意的。 那,或許你又會問: * 有辦法將 stderr 也餵進下一個命令的 stdin 去嗎? 方法當然是有,而且你早已學過了﹗ ^_^ 我提示一下就好: * 請問你如何將 stderr 合?氵M stdout 一同輸出呢? 或許,你仍意尤未盡﹗或許,你曾經碰到過下面的問題: * 在 cm1 | cm2 | cm3 ... 這段 pipe line 中,若要將 cm2 的結果存到某一檔案呢? 若你寫成 cm1 | cm2 > file | cm3 的話, 那你肯定會發現 cm3 的 stdin 是空的﹗(當然啦,你都將水管接到別的水池了﹗) 聰明的你或許會如此解決: cm1 | cm2 > file ; cm3 < file 是的,你的確可以這樣做,但最大的壞處是:這樣一?恚琭ile I/O 會變雙倍﹗ 在 command 執行的整個過程中,file I/O 是最常見的最大效能殺手。 凡是有經驗的 shell 操作者,都會盡量避免或降低 file I/O 的頻率。 那,上面問題還有更好方法嗎? 有的,那就是 tee 命令了。 * 所謂 tee 命令是在不影響原本 I/O 的情況下,將 stdout 複製一份到檔案去。 因此,上面的命令行可以如此打: cm1 | cm2 | tee file | cm3 在預設上,tee 會改寫目標檔案,若你要改為增加內容的話,那可用 -a 參數達成。 基本上,pipe line 的應用在 shell 操作上是非常?V泛的,尤其是在 text filtering 方面, 凡舉 cat, more, head, tail, wc, expand, tr, grep, sed, awk, ... 等等文字處理工具, 搭配起 pipe line ?硎褂茫銜驚覺 command line 原?硎腔畹萌绱司实末u 6.exec e x e c命令可以用来替代当前s h e l l;换句话说,并没有启动子s h e l l。使用这一命令时任何现有环境都将会被清除,并重新启动一个s h e l l。它的一般形式为: 代码: exec command 我 所能够想像得出的描述e x e c命令最贴切的说法就是:当这个脚本结束时,相应的会话可能就结束了。e x e c命令的一个常见用法就是在用户的. p r o f i l e最后执行时,用它来执行一些用于增强安全性的脚本。如果用户的输入无效,该s h e l l将被关闭,然后重新回到登录提示符。e x e c还常常被用来通过文件描述符打开文件。文件描述符:file descriptor(FD) e x e c在对文件描述符进行操作的时候(也只有在这时),它不会覆盖你当前的s h e l l。 source和exec的区别 1,他们带的参数不一样 source通常是shell脚本,而exec不但可以把一个脚本当成参数,而且还可以把一个系统命令当参数,例如: exec ls 2,另外一个不同就是,exec任务执行完毕后,会执行类似logout的操作,而source执行完一个任务后返回当前的shell. |