1. 清空文件內(nèi)容
$ > file
這一行命令用到了輸出重定向操作符>。輸出重定向發(fā)生時(shí)驮肉,文件會(huì)被打開準(zhǔn)備寫入涣雕。如果此時(shí)文件不存在則先創(chuàng)建,存在則將其大小截取為0装处。這里我們并沒有重定向?qū)懭魏蝺?nèi)容到文件中误债,所以文件依然保持為空浸船。
如果你想替換文件的內(nèi)容,或者創(chuàng)建一個(gè)包含指定內(nèi)容的文件寝蹈,可以運(yùn)行下面的命令:
$ echo "some string" > file
2. 追加內(nèi)容到文件
$ echo "foo bar baz" >> file
這一行命令用到了另外一個(gè)輸出重定向操作符>>李命,該操作符將內(nèi)容追加到文件。同樣地箫老,如果文件不存在則先創(chuàng)建它封字。追加的內(nèi)容之后,緊跟著換行符耍鬓。如果你不想要追加換行符阔籽,在執(zhí)行echo命令時(shí)可以指定-n選項(xiàng):
$ echo -n "foo bar baz" >> file
3. 讀取文件的首行并賦值給變量
$ read -r line < file
這一行命令用到了 Bash 的內(nèi)置命令read,和輸入重定向操作符<牲蜀。read命令從標(biāo)準(zhǔn)輸入中讀取一行笆制,并將內(nèi)容保存到變量line中。在這里各薇,-r選項(xiàng)保證讀入的內(nèi)容是原始的內(nèi)容项贺,意味著反斜杠轉(zhuǎn)義的行為不會(huì)發(fā)生。輸入重定向操作符< file打開并讀取文件file峭判,然后將它作為read命令的標(biāo)準(zhǔn)輸入开缎。
記住,read命令會(huì)刪除包含在IFS變量中出現(xiàn)的所有字符林螃,IFS 的全稱是 Internal Field Separator奕删,Bash 根據(jù) IFS 中定義的字符來分隔單詞。在這里疗认,read命令讀入的行被分隔成多個(gè)單詞完残。默認(rèn)情況下,IFS包含空格横漏,制表符和回車谨设,這意味著開頭和結(jié)尾的空格和制表符都會(huì)被刪除。如果你想保留這些符號(hào)缎浇,可以通過設(shè)置IFS為空來完成:
$ IFS= read -r line < file
IFS 的變化僅會(huì)影響當(dāng)前的命令扎拣,這行命令可以保證讀入原始的首行內(nèi)容到變量line中,同時(shí)行首與行尾的空白字符被保留素跺。
另外一種讀取文件首行內(nèi)容二蓝,并賦值給變量的方法是:
$ line=$(head -1 file)
這里用到了命令替換操作符$(...),它運(yùn)行括號(hào)里的命令并且將輸出返回指厌。 這個(gè)例子中刊愚,命令是head -1 file,輸出的內(nèi)容是文件的首行踩验。輸入然后通過等號(hào)賦值給變量line鸥诽。$(...)的等價(jià)寫法是`...`商玫,所以也可以換成下面這樣:
$ line=`head -1 file`
不過,在 Bash 中$(...)用法更加推薦衙传,因?yàn)樗雌饋砀诱麧嵕鎏⑶胰菀浊短资褂谩?/p>
4. 依次讀入文件每一行
$ while read -r line; do
? ? # do something with $line
done < file
這是一種正確的讀取文件內(nèi)容的做法,read命令放在while循環(huán)中蓖捶。當(dāng)read命令遇到文件結(jié)尾時(shí)(EOF)地回,它會(huì)返回一個(gè)正值,導(dǎo)致循環(huán)判斷失敗終止俊鱼。
記住刻像,read命令會(huì)刪除首尾多余的空白字符,所以如果你想保留并闲,請(qǐng)?jiān)O(shè)置 IFS 為空值:
$ while IFS= read -r line; do
? ? # do something with $line
done < file
如果你不想將< file放在最后细睡,可以通過管道將文件的內(nèi)容輸入到 while 循環(huán)中:
$ cat file | while IFS= read -r line; do
? ? # do something with $line
done
5. 隨機(jī)讀取一行并賦值給變量
$ read -r random_line < <(shuf file)
Bash 中并沒有提供一種直接的方法來隨機(jī)讀取文件的某一行內(nèi)容,所以這里需要利用外部程序帝火。在最新的一些 Linux 系統(tǒng)上溜徙,GNU Coreutils 包中提供的shuf命令可以滿足我們的需求。
這一行命令中用到了進(jìn)程替換(process substitution)操作符<(...)犀填。進(jìn)程替換操作會(huì)創(chuàng)建一個(gè)匿名的管道文件蠢壹,并將進(jìn)程命令的標(biāo)準(zhǔn)輸出連接到管道的寫一端。然后 Bash 開始執(zhí)行進(jìn)程替換中的命令九巡,然后將整個(gè)進(jìn)程替換的表達(dá)式替換成匿名管道的文件名图贸。
當(dāng) Bash 看到<(shuf file)時(shí),它首先打開一個(gè)特殊的文件/dev/fd/n冕广,這里的n是一個(gè)空閑的文件描述符疏日,然后執(zhí)行shuf file命令,將標(biāo)準(zhǔn)輸出連接到/dev/fd/n撒汉,并且替換<(shuf file)為/dev/fd/n沟优,因此實(shí)際的命令會(huì)變成:
$ read -r random_line < /dev/fd/n
結(jié)果會(huì)讀取洗牌后的文件的第一行內(nèi)容。
另外一種做法是睬辐,使用 GNU sort 命令挠阁,它提供的-R選項(xiàng)可以隨機(jī)排序文件:
$ read -r random_line < <(sort -R file)
或者,同前面一樣溉委,將結(jié)果賦值給變量:
$ random_line=$(sort -R file | head -1)
這里鹃唯,我們首先通過sort -R隨機(jī)排序文件爱榕,然后通過head -1讀取文件的第一行瓣喊。
6. 讀取文件首行前三個(gè)字段并賦值給變量
$ while read -r field1 field2 field3 throwaway; do
? ? # do something with $field1, $field2, and $field3
done < file
如果在read命令中指定多個(gè)變量名,它會(huì)將讀入的內(nèi)容分隔成多個(gè)字段黔酥,然后依次賦值給對(duì)應(yīng)的變量藻三,第一個(gè)字段賦值給第一個(gè)變量洪橘,第二個(gè)字段賦值給第二個(gè)變量,等等棵帽,最后將剩余的所有字段賦值給最后一個(gè)變量熄求。這也是為什么,在上面的例子中逗概,我們加了一個(gè)throwaway變量弟晚,否則的話,當(dāng)文件的一行大于三個(gè)字段時(shí)逾苫,第三個(gè)變量的內(nèi)容會(huì)包含所有剩余的字段卿城。
有時(shí)候,為了書寫方便铅搓,可以簡(jiǎn)單地用_來替換throwaway變量:
$ while read -r field1 field2 field3 _; do
? ? # do something with $field1, $field2, and $field3
done < file
又或者瑟押,如果你的文件確實(shí)只有三個(gè)字段,那可以忽略它:
$ while read -r field1 field2 field3; do
? ? # do something with $field1, $field2, and $field3
done < file
下面是一個(gè)例子星掰,假如你想知道一個(gè)文件到底包含多少行多望,多少個(gè)單詞以及多少個(gè)字節(jié)。當(dāng)你執(zhí)行wc命令時(shí)氢烘,你會(huì)得到3個(gè)數(shù)字加上文件名怀偷,文件名在最后:
$ cat file-with-5-lines
x 1
x 2
x 3
x 4
x 5
$ wc file-with-5-lines
5 10 20 file-with-5-lines
所以,這個(gè)文件包含5行威始,10個(gè)單詞枢纠,以及20個(gè)字符。我們接下來黎棠,可以通過read命令將這些信息保存到變量中:
$ read lines words chars _ < <(wc file-with-5-lines)
$ echo $lines
5
$ echo $words
10
$ echo $chars
20
類似地晋渺,你也可以使用here-strings將字符串分隔并保存到變量中。假設(shè)你有一個(gè)字符串變量$info脓斩,內(nèi)容為"20 packets in 10 seconds"木西,然后你想要將從中獲取20和10。在不久之前随静,我是這樣來完成的:
$ packets=$(echo $info | awk '{ print $1 }')
$ time=$(echo $info | awk '{ print $4 }')
然而八千,得益于read命令的強(qiáng)大和對(duì) Bash 的了解,我們可以這樣做:
$ read packets _ _ time _ <<< "$info"
這里燎猛,<<<就是 here-string 的語法恋捆,它允許你直接傳遞字符串給標(biāo)準(zhǔn)輸入。
7. 保存文件的大小到變量
$ size=$(wc -c < file)
這一行命令中用到了第3點(diǎn)中介紹的命令替換操作$(...)重绷,它運(yùn)行里面的命令并將結(jié)果獲取回來沸停。在這個(gè)例子中,命令是wc -c < file昭卓,它輸出文件的字節(jié)數(shù)愤钾。這個(gè)結(jié)果最終會(huì)賦值給變量size瘟滨。
8. 從文件路徑中獲取文件名
假設(shè),你有一個(gè)文件能颁,它的路徑為/path/to/file.ext杂瘸,然后你要從中獲取文件名,在這里是file.ext伙菊。你要怎么做败玉? 一個(gè)好的方法是通過參數(shù)展開(parameter expansion)功能:
$ filename=${path##*/}
這一行命令使用了參數(shù)展開的語法:${var##pattern},它從$var字符串開始處開始匹配pattern镜硕。如果能夠匹配成功绒怨,將最長(zhǎng)匹配的內(nèi)容刪除后再返回。
在這個(gè)例子中谦疾,匹配的模式是*/南蹂,它嘗試匹配/path/to/file.ext的開始部分,正如前面所說念恍,這里是貪婪匹配六剥,所以它能夠匹配到最后一個(gè)斜杠為止,即匹配的內(nèi)容是/path/to/峰伙。所以當(dāng)把匹配的內(nèi)容刪除后疗疟,返回的內(nèi)容就是文件名file.ext。
9. 從文件路徑中獲取目錄名
和上面一樣類似瞳氓,這次你要從路徑/path/to/file.txt中獲取目錄名/path/to策彤。你可以繼續(xù)通過參數(shù)展開功能來完成這個(gè)任務(wù):
$ dirname=${path%/*}
這次的用法是${var%pattern},它從$var的結(jié)尾處匹配/*匣摘。如果能夠成功匹配店诗,將最短匹配的內(nèi)容刪除再返回。
在這個(gè)例子中音榜,匹配的模式是/*庞瘸,它能夠匹配/file.ext部分,刪除這部分內(nèi)容后返回的就是目錄名稱赠叼。
10. 快速拷貝文件
假設(shè)你要將文件/path/to/fil拷貝到/path/to/file_copy擦囊,一般情況下,大多數(shù)人會(huì)這么來寫:
$ cp /path/to/file /path/to/file_copy
不過,你可以利用括號(hào)展開(brace expansion){...}功能:
$ cp /path/to/file {,_copy}
括號(hào)展開可以生成任意字符串的組合,在這個(gè)例子中躲庄,/path/to/file{,_copy}最終生成/path/to/file /path/to/file_copy。所以上面這行命令最終發(fā)型成:
$ cp /path/to/file /path/to/file_copy
類似地贯被,你可以執(zhí)行下面的命令快速的移動(dòng)文件:
$ mv /path/to/file{,_old}
這行命令展開后就變成了:
$ mv /path/to/file /path/to/file_old