無論是要交給程序處理的數(shù)據(jù),還是控制腳本的簡(jiǎn)單命令,都少不了輸入和輸出。程序要做的第一件事就是處理如同一陰一陽的 “輸入與輸出”。
1 、從文件獲取輸入
當(dāng)我們希望向文件輸出內(nèi)容時(shí),我們可以通過符號(hào) > 或 >> 實(shí)現(xiàn)。而用代表輸入重定向的符號(hào) < 可以從文件中讀取數(shù)據(jù),如下:
$ wc < my.file之所以選擇這種形狀的操作符號(hào),原因在于它們可以從視覺上提示重定向的方向。 很多 shell 命令可以接受一個(gè)或多個(gè)文件名作為參數(shù),但如果沒有給出文件名,命令就會(huì)從標(biāo)準(zhǔn)輸入讀取。使用這種命令時(shí),可以采用?command filename?或者?command < filename,這兩種形式的結(jié)果沒什么區(qū)別。在這個(gè)例子中,wc 是這樣,換作 cat 或其他命令,也是如此。
?
2、將數(shù)據(jù)與腳本存放在一起
< 可以從文件讀取數(shù)據(jù),當(dāng)你需要獲得腳本輸入,但又不想用單獨(dú)的文件時(shí),使用 <<(here-document)從命令行而非文件重定向輸入文本。如果放在 shell 腳本中,則腳本文件可以同時(shí)包含數(shù)據(jù)與代碼。 以下是名為 ext.sh 的 shell 腳本示例:
# 下面是here-document grep $1 <?
當(dāng)我們運(yùn)行此腳本,可以傳入一個(gè)參數(shù),如下調(diào)用:
?
$ ./ext.sh bill # 輸出以下內(nèi)容 bill x.919grep 命令查找第一個(gè)參數(shù)是否在指定文件中出現(xiàn),如果沒有指定文件,那么它會(huì)在標(biāo)準(zhǔn)輸入中查找。通過設(shè)置 here document,告訴 shell 將標(biāo)準(zhǔn)輸入重定向(臨時(shí))到此處。<< 語法表示我們想創(chuàng)建一個(gè)臨時(shí)輸入源,EOF 是一個(gè)任意的字符串(你想用什么都行),用作臨時(shí)輸入的終止符。它并不屬于輸入的一部分,只是作為標(biāo)記告訴輸入在哪里結(jié)束。?
3、避免?here-document?中的怪異行為
here-document 在使用時(shí)可能會(huì)出現(xiàn)一些怪異的行為。你想用上一節(jié)介紹的方法來保存一份簡(jiǎn)單的捐贈(zèng)人列表,因此創(chuàng)建了一個(gè)名為 donors.sh 的文件,如下所示:
# 簡(jiǎn)單地查找慷慨的捐贈(zèng)人 grep $1 <?
但是運(yùn)行時(shí)出現(xiàn)了奇怪的輸出:
?
$ ./donors.sh bill pete bill00 bill $ 9 $ ./donors.sh pete pete pete00?
正常情況下(除非使用了轉(zhuǎn)義語法),bash 手冊(cè)頁中是這樣說的:“……h(huán)ere-document 的每一行都要執(zhí)行參數(shù)擴(kuò)展、命令替換以及算術(shù)擴(kuò)展”。因此,最初的 donors 腳本中所發(fā)生的事情是捐贈(zèng)額被當(dāng)作 shell 變量了。例如,$100 被視為 shell 變量 $1,隨后跟著兩個(gè) 0。這就是為什么我們?cè)谒阉?“pete” 時(shí),得到的是 pete00;搜索 “bill” 時(shí),得到的是 bill00。
解決辦法:
通過轉(zhuǎn)義結(jié)尾標(biāo)記中的任意或所有字符,修改腳本內(nèi)容,關(guān)閉 here-document 內(nèi)部的 shell 特性 (注意觀察 EOF 位置的變化):
?
# 簡(jiǎn)單地查找慷慨的捐贈(zèng)人 grep $1 <<'EOF' pete $100 joe $200 sam $ 25 bill $ 9 EOF?
盡管其中存在非常微妙的區(qū)別,但也可以將 <
?
$ ./donors.sh pete pete $100?
4、獲取用戶輸入
輸入不止從文件中獲取,有時(shí)我們還需要獲取用戶輸入的內(nèi)容。此時(shí),我們需要用到 read 命令,如下:
$ read?
或者
?
$ read -p "answer me this " ANSWER?
不帶參數(shù)的 read 語句會(huì)讀取用戶輸入并將其保存在 shell 變量 REPLY 中,這是 read 的最簡(jiǎn)形式。如果希望 bash 在讀取用戶輸入前先輸出提示信息,可以使用 -p 選項(xiàng)。-p 之后的單詞就是提示信息,如果想提供多個(gè)單詞,可以將其引用起來。記住,要在提示信息結(jié)尾處加上標(biāo)點(diǎn)符號(hào)或空格,因?yàn)楣鈽?biāo)會(huì)停在那里等待輸入。-t 選項(xiàng)可以設(shè)置超時(shí)值。指定秒數(shù)達(dá)到后,不管用戶是否輸入,read 語句都會(huì)返回。我們的示例同時(shí)用到了 -t 和 -p 選項(xiàng),但你也可以單獨(dú)使用 -t 選項(xiàng)。
上面的方式獲取用戶輸入時(shí)會(huì)以明文回顯,那適用密碼輸入么?
當(dāng)我們需要用戶輸入敏感信息時(shí),需要禁止用戶輸入內(nèi)容的回顯。此時(shí)用 read 命令讀取用戶輸入,需要加上一個(gè)特殊選項(xiàng)來關(guān)閉回顯:
?
read -s -p "password: " PASSWD printf "%b" " "?
-s 選項(xiàng)告訴 read 命令不要回顯輸入的字符(s 代表 silent),-p 選項(xiàng)指明下一個(gè)參數(shù)是提示信息,會(huì)在讀取用戶輸入之前顯示。從用戶那里讀取到的輸入行保存在變量 $PASSWD 中。在 read 之后,我們用 printf 輸出了一個(gè)換行符。這里的 printf 不能少,因?yàn)?read -s 會(huì)關(guān)閉字符回顯。如果禁止了回顯功能,當(dāng)用戶按下回車鍵時(shí),就不會(huì)回顯換行符,后續(xù)輸出就會(huì)和提示信息出現(xiàn)在同一行。輸出換行符會(huì)將光標(biāo)帶到下一行。
當(dāng)然,我們也可以選擇一行,如下:
?
read -s -p "password: " PASSWD ; printf "%b" " "?
Shell 標(biāo)準(zhǔn)輸出
如果無法產(chǎn)生輸出,那么軟件也就沒什么價(jià)值了,但長(zhǎng)久以來,I/O 一直是難纏的計(jì)算領(lǐng)域之一。問題是有太多類型的輸出,向屏幕寫入不同于向文件寫入,向文件寫入也不同于向磁帶或閃存寫入。所以,對(duì)于輸出會(huì)產(chǎn)生一些問題,如下:
軟件開發(fā)人員是否要針對(duì)各種輸出設(shè)備編寫代碼,甚至包括尚未發(fā)明的設(shè)備?
寫到哪個(gè)文件?程序怎么知道是該寫入代表終端窗口的文件、磁盤文件還是其他種類的文件?
顯然,如果把這些事情都交給每個(gè)程序員是不合理的,所以這種事情留給 shell 就行了。
1、輸出到終端 / 終端窗口
想要用 shell 命令產(chǎn)生一些簡(jiǎn)單的輸出,使用內(nèi)建命令 echo。命令行中的所有參數(shù)都會(huì)打印到屏幕上。
echo Please wait.?
輸出:
?
Please wait.結(jié)果和在 bash 提示符(字符 $)后輸入該命令相同:
echo 是最簡(jiǎn)單的 bash 命令之一。該命令可以將參數(shù)輸出到屏幕上。但是有幾點(diǎn)需要記?。??
首先,shell 負(fù)責(zé)解析 echo 的命令行參數(shù)。將參數(shù)交給 echo 前,shell 會(huì)完成所有的替換、通配符匹配等操作。
其次,在解析參數(shù)時(shí),參數(shù)之間的空白字符會(huì)被忽略,如下圖:
shell 對(duì)參數(shù)間的空白字符沒有太多限制,這通常是一種不錯(cuò)的特性。但對(duì)于 echo 來說,就有點(diǎn)煩人了。保留輸出中的空白字符。將字符放入引號(hào)中就可以保留空白字符,如下圖:
引號(hào)中的單詞組成了 echo 命令的單個(gè)參數(shù)。該參數(shù)是一個(gè)字符串,shell 不會(huì)干涉字符串的內(nèi)容。實(shí)際上可以用單引號(hào)('')明確告訴 shell 不要干涉字符串。2、在輸出中加入更多格式控制
使用內(nèi)建命令 printf。例如:
printf '%s = %d ' Lines $LINES Lines = 24?
或者:
?
$ printf '%-10.10s = %4.2f ' 'Gigahertz' 1.92735 Gigahertz = 1.93內(nèi)建命令 printf 的行為和 C 語言中的同名庫函數(shù)相似,其中第一個(gè)參數(shù)是格式控制字符串,之后的參數(shù)都根據(jù)格式規(guī)范(%)進(jìn)行格式化。 % 和格式類型(本例為 s 或 f)之間的數(shù)字提供了額外的格式化細(xì)節(jié)。 對(duì)于浮點(diǎn)類型(f),第一個(gè)數(shù)字(指示符 4.2 中的 4)是整個(gè)字段的寬度。第二個(gè)數(shù)字(2)是應(yīng)該在小數(shù)點(diǎn)右側(cè)打印出的數(shù)位量。注意,結(jié)果會(huì)按照四舍五入處理。 對(duì)于字符串,第一個(gè)數(shù)字是字段的最大寬度,第二個(gè)數(shù)字是要輸出的字符數(shù)量。根據(jù)需要,字符串會(huì)被截?cái)啵ㄩL(zhǎng)于?max)或用空白填充(不足?min)。如果指示符?max?和?min?相同,那么就可以確保字符串按照該長(zhǎng)度輸出。指示符左側(cè)的負(fù)號(hào)表示字符串向左對(duì)齊(在字段寬度內(nèi))。如果不使用負(fù)號(hào),則字符串向右對(duì)齊?
3、消除輸出中的換行符
希望輸出中不包含 echo 默認(rèn)生成的換行符。使用 printf,做法很簡(jiǎn)單,去掉格式化字符串末尾的 即可,如下圖:
printf "%s %s" next prompt
如果是 echo,則使用 -n 選項(xiàng):$ echo -n prompt
因?yàn)?printf 的格式字符串(第一個(gè)參數(shù))末尾并沒有換行符,所以命令行提示符($)出現(xiàn)在了 printf 的輸出之后。該特性在 shell 腳本中用處更大,你可能希望在形成一整行前由多條語句逐部分輸出,或者在讀取輸入前顯示用戶提示。 換作 echo 命令(參見 15.6 節(jié)),消除換行符的方法有兩種。 首先,-n 選項(xiàng)能夠抑制輸出行尾的換行符。 另外,echo 命令還可以處理多種具有特殊含義的轉(zhuǎn)義序列(如表示換行符的 ),這些轉(zhuǎn)移序列與 C 語言字符串中的類似。調(diào)用 echo 命令時(shí)加上 -e 選項(xiàng)。其中一種轉(zhuǎn)義序列是 C,它并不會(huì)輸出什么字符,而是禁止在行尾輸出換行符。如下圖:$ echo -e 'hic'?
4、保存命令輸出
如過想把命令輸出保存在文件中,用 > 符號(hào)告訴 shell 將輸出重定向至文件,例如:
$ echo fill it up fill it up $ echo fill it up > file.txt我們來查看一下文件 file.txt 的內(nèi)容,看看其中是否包含了命令的輸出:$ cat file.txt fill it up示例第一部分的第一行中出現(xiàn)的 echo 命令包含了 3 個(gè)要輸出的參數(shù)。第二行用 > 將這些輸出保存到文件 file.txt 中,這就是看不 到 echo 輸出的原因。 示例第二部分用 cat 命令顯示文件內(nèi)容。我們可以看出,文件中包含的正是 echo 本該輸出的內(nèi)容。 cat 命令得名自一個(gè)較長(zhǎng)的單詞 concatenation(拼接)。該命令會(huì)將出現(xiàn)在命令行上的文件的輸出拼接在一起,如果你輸入 cat file1 file2 file3,那么這些文件的內(nèi)容會(huì)逐個(gè)發(fā)送到終端窗口。如果一個(gè)大文件被分成了兩半,你也可以用 cat 將其恢復(fù)原樣(也就是將兩部分拼接起來),這只需將輸出保存到另一個(gè)文件中:cat first.half second.half > mergeFile.txt?
5、將輸出保存到其他文件
如想要用重定向?qū)⑤敵霰4娴疆?dāng)前目錄之外的其他位置,重定向輸出時(shí)加上路徑,如下:
echo some more data > /tmp/echo.out?
或者:
?
echo some more data > ../../over.here出現(xiàn)在重定向符號(hào)(>)后的文件名其實(shí)就是路徑名。如果沒有任何限定部分,那么文件就會(huì)放置在當(dāng)前目錄中。 如果文件名以斜線(/)起始是絕對(duì)路徑名,此時(shí)文件會(huì)被放置在文件系統(tǒng)層次結(jié)構(gòu)(目錄樹)中以根目錄起始的指定位置。 第二個(gè)例子中,我們使用了相對(duì)路徑名 ../../over.here,其中的.. 是一個(gè)指向父目錄的特殊目錄,存在于每個(gè)目錄中。?
6、將輸出和錯(cuò)誤消息發(fā)送到不同文件
希望獲得程序的輸出,但不想輸出被出現(xiàn)的錯(cuò)誤消息弄亂。要保存的錯(cuò)誤消息混雜在程序輸出中不容易找出,可將輸出和錯(cuò)誤消息重定向到不同文件,如下:
$ myprogram 1> messages.out 2> message.err?
或者采用更常見的方法:
?
$ myprogram > messages.out 2> message.errshell 會(huì)創(chuàng)建兩個(gè)輸出文件。 第一個(gè)是 messages.out,程序 myprogram 的所有輸出都會(huì)重定向到該文件。 第二個(gè)是 message.err,程序 myprogram 的所有錯(cuò)誤消息都會(huì)重定向到 message.err。 在 1> 和 2> 中,數(shù)字表示文件 描述符。?
1 代表標(biāo)準(zhǔn)輸出(STDOUT),
2 代表標(biāo)準(zhǔn)錯(cuò)誤(STDERR)。
0 代表標(biāo)準(zhǔn)輸入(STDIN)。
如果不指定數(shù)字,則假定為 STDOUT。
7、將輸出和錯(cuò)誤消息發(fā)送到同一文件
利用重定向,我們可以將輸出或錯(cuò)誤消息保存到單獨(dú)的文件中,但如何將兩者送往同一文件呢?用 shell 語法將標(biāo)準(zhǔn)錯(cuò)誤消息重定向到和標(biāo)準(zhǔn)輸出相同的地方。 首選:
$ myprogram >& outfile?
或者:
?
$ myprogram &> outfile?
又或者老式且略煩瑣的寫法:
?
$ myprogram > outfile 2>&1其中,myprogram 是準(zhǔn)備向 STDERR 和 STDOUT 生成輸出的程序。 &> 和 >& 只是將 STDOUT 和 STDERR 發(fā)送到相同地方的便捷寫法。?
8、追加輸出
每次重定向輸出,都會(huì)產(chǎn)生一個(gè)全新的輸出文件。如果想要兩次(或三次、四次……)重定向輸出,同時(shí)又不想破壞之前的輸出,該怎么辦呢? 在 bash 的重定向符號(hào)中,雙大于號(hào)(>>)表示追加輸出:
$ ls > /tmp/ls.out $ cd ../elsewhere $ ls >> /tmp/ls.out $ cd ../anotherdir $ ls >> /tmp/ls.out如果存在同名文件,第一行中的重定向會(huì)將其截?cái)啵?ls 命令的輸出保存在這個(gè)已被清空的文件中。 后兩次調(diào)用 ls 時(shí)使用了雙大于號(hào)(>>),表示向輸出文件中追加內(nèi)容,而不是覆蓋其原有內(nèi)容。 如果想要同時(shí)重定向錯(cuò)誤消息(STDERR),可以將 STDERR 的重定向放在后面,如下所示:ls >> /tmp/ls.out 2>&1?
在 bash 4 中,你可以將這兩個(gè)重定向合二為一:
?
$ ls &>> /tmp/ls.out該命令會(huì)重定向 STDERR 和 STDOUT,并將兩者追加到指定文件中。& 符號(hào)必須先出現(xiàn),且這 3 個(gè)字符之間不能有空格?
9、丟棄輸出
你有時(shí)不想將輸出保存到文件中或者有時(shí)甚至不想看到輸出。如我們?cè)诓檎夷硞€(gè)文件時(shí),忽略那些沒有權(quán)限的提示,如下圖:
?此時(shí),我們可以將輸出重定向到 /dev/null,如下所示:$ find / -name myfile 2> /dev/null其實(shí),你可以將不想要的輸出重定向到文件,然后再將其刪除。但還有一個(gè)更簡(jiǎn)單的方法。Unix 和 Linux 系統(tǒng)都存在一個(gè)特殊設(shè)備,該設(shè)備并非真實(shí)的硬件,而僅僅是一個(gè)位桶(bit bucket),我們可以將不需要的數(shù)據(jù)都扔進(jìn)去。它就是 /dev/null,非常適用于此類場(chǎng)景。寫入其中的數(shù)據(jù)會(huì)被直接丟棄并不會(huì)占用磁盤空間,重定向很容易做到這一點(diǎn)。示例中,只有發(fā)往標(biāo)準(zhǔn)錯(cuò)誤的輸出被丟棄了?
審核編輯:湯梓紅
評(píng)論
查看更多