grep
grep
命令很容易學習,它主要有兩種使用方式,一種是單獨使用,比如搜索某個文件中的內容:
或者從標準輸入中搜索內容:
要想掌握好 grep
,重點在於瞭解它的各種參數。下面是一些常用的參數,如果不記得,後續可以用 man grep
命令來查閱。
grep 在搜索時,默認是大小寫敏感的,但如果要搜索 mysql,它可能寫做 mysql 也可能寫做 MySQL,這就可能存在搜索不到的問題,此時可以用 -i
參數:
如果使用 -n
參數可以打印匹配行的行號,使用 -H
參數可以打印匹配文件的文件名。
默認情況下,如果某個二進制文件中含有搜索的關鍵詞,會顯示 Binary file ... matches,使用 -I
選項可以忽略二進制文件,使用 -a
選項可以把二進制文件當做文本文件來處理,從而輸出匹配的部分。
默認情況下 grep
會展示匹配的那一行,如果想查看上下文,可以使用 -A
、-B
和 -C
這三個參數:
-A 3
:展示匹配行以及後面的 3 行-B 3
:展示匹配行以及前面的 3 行-C 3
:展示匹配行以及前後的 3 行,等價於-A 3 -B 3
另外一些常用的選項包括 -v
,表示只顯示那些不匹配的行,-o
表示只顯示匹配的部分,-q
表示不輸出內容,通常與 if
連用。
在前面的章節中我們介紹過,可以通過管道將多個命令串聯起來,前提是管道後面的命令要支持從標準輸入中讀取數據,比如前文的 grep
命令。
然而有些命令並不支持從標準輸入中讀取,比如這樣寫是無效的:
此時我們可以藉助 xargs
命令:
這條命令的原理是,xargs
會把換行符、空格、製表符、EOF等符號做爲分隔符,把輸入的內容切分爲一個數組,並把數組中每一個元素作爲參數,放到後面的命令中執行,用僞代碼來寫就是:
很常見的一個坑就是,如果文件名帶有空格,比如 hello world 就會被 xargs
截斷爲兩個參數,顯然不符合預期。不過一般對內容或者文件進行過濾時,我們都會使用 grep
或 find
,這兩個命令都有辦法配合 xargs
。
用 grep
的話會繁瑣一些,需要用 tr
命令把換行符轉換成特殊字符 \0
,再利用 xargs
的 -0
參數,根據文檔所述,這個參數會把分隔符指定爲 -0
,從而避免了文件名中含有空格的影響。
用 find
也是類似的原理:
只不過它自帶了 -print0
選項,寫法更簡單。
sed 誕生於 1977 年,已經 41 歲了,這麼一位叔叔級別的命令至今還活躍在各種 Shell 腳本中,由此可見它是多麼重要。
Mac 自帶的時 BSD 版本的 sed
,因爲功能較弱,我不推薦使用,建議使用 gsed
,如無特殊說明,下文的介紹都是針對 gsed
的。
sed
和 grep
的用法類似,都是 sed pattern file
或者 echo 'xx' | sed pattern
,也就是說第二個參數可以是文件,也可以從標準輸入流中讀取。
最標準的用法是進行文本替換(也可以用 tr
命令實現):
有時候我們可能不止使用一次 sed
,此時可以用 -e
參數把多個命令串聯起來:
在 gsed
中,還可以使用 Shell 裏定義的變量:
我推薦用 gsed 是因爲它有一個 -i
選項,可以對文件進行原地修改:
sed
最核心的部分在於這裏的 s/a/aa/g
,它由若干個斜槓組成(其實也不一定要用斜槓,只要保持一致就行)。這裏的 s
表示替換,a
表示待匹配的內容,支持正則,aa
表示替換後的內容,g
表示全部替換,更多的用法有:
1,3s/a/aa/g
:只替換第一到三行中的內容s/a/aa/1
:只替換每行第一個a
s/a/aa/2g
:每行前兩個a
不替換,從第三個開始替換2i sss
:在第二行前面追加一行,內容爲sss
1a sss
:在第一行後面追加一行,內容爲sss
,等價於2i sss
/a/a sss
:遇到有字母 a 的行,就在後面追加一行sss
1c sss
:把第一行替換爲sss
2d
:刪除第二行
這些用法雖然看起來複雜,但是和 vim 一樣,每個部分就幾種寫法,然後自行排列組合即可。
gsed
在默認情況下,會把輸入的每一行都輸出一遍,它有一個常用的選項是 -n
,表示不輸出任何一行。通常與 p
命令合用,這個命令可以打印匹配的行,類似於 grep
的效果。
awk
是和 sed
同時代的命令,並稱爲文本處理兩大神器。個人認爲 sed
的強大之處在於文本匹配後的處理,而 awk
則更適合文本的結構化處理。
這裏以獲取 ip 地址的命令來介紹下:
這裏 awk
的用法其實很簡單,就是打印第二列。awk 的核心在於內建的變量:
$0 | 當前記錄(這個變量中存放着整個行的內容) |
$1~$n | 當前記錄的第n個字段,字段間由FS分隔 |
FS | 輸入字段分隔符 默認是空格或Tab |
NF | 當前記錄中的字段個數,就是有多少列 |
NR | 已經讀出的記錄數,就是行號,從1開始,如果有多個文件話,這個值也是不斷累加中。 |
FNR | 當前記錄數,與NR不同的是,這個值會是各個文件自己的行號 |
RS | 輸入的記錄分隔符, 默認爲換行符 |
awk
一個很常見的用法是 -f
參數,可以指定輸入字段的分隔符:
其實理論上來說,awk
比 sed
還要強大,因爲它是一個圖靈完備的語言,支持 for 循環等等編程思想。建議感興趣的讀者閱讀 AWK 簡明教程 瞭解更多 awk 的使用技巧
備註:
1.管道是把一個命令的輸出傳遞給另一個命令作爲輸入,比如:
command1 | command2
但是command2僅僅把command1輸出的內容作爲輸入參數。
find . -name "install.log" -print打印出的是install.log這個字符串,如果僅僅使用管道,那麼command2能夠使用的僅僅是install.log這個字符串,不能把它當作文件來進行處理。xargs就是爲了能夠對find搜索到的文件進行操作而編寫的,它能把管道傳來的字符串當作文件交給其後的命令執行。
舉個例子:
(1)$find . -name "install.log" -print | cat
./install.log #顯示從管道傳來的內容,僅僅作爲字符串來處理
(2)$find . -name "install.log" -print | xargs cat
aaaaaa #將管道傳來的內容作爲文件,交給cat執行。也就是說,該命令執行的是如果存在install.log,那麼就打印出這個文件的內容。
通過這個例子,應該很容易理解這樣有什麼不同了。當你要對匹配文件操作時,使用find and xargs,其實這都是運用了管道。xargs是shell命令的一個,可以把管道輸入的內容轉化爲其參數要操作的文件。