grep、xargs、sed、awk、xargs

grep

grep 命令很容易學習,它主要有兩種使用方式,一種是單獨使用,比如搜索某個文件中的內容:

grep 'content' file.txt

或者從標準輸入中搜索內容:

echo 'something' | grep 'some'

要想掌握好 grep,重點在於瞭解它的各種參數。下面是一些常用的參數,如果不記得,後續可以用 man grep 命令來查閱。

grep 在搜索時,默認是大小寫敏感的,但如果要搜索 mysql,它可能寫做 mysql 也可能寫做 MySQL,這就可能存在搜索不到的問題,此時可以用 -i 參數:

echo 'MySQL' | grep -i 'mysql'

如果使用 -n 參數可以打印匹配行的行號,使用 -H 參數可以打印匹配文件的文件名。

默認情況下,如果某個二進制文件中含有搜索的關鍵詞,會顯示 Binary file ... matches,使用 -I 選項可以忽略二進制文件,使用 -a選項可以把二進制文件當做文本文件來處理,從而輸出匹配的部分。

默認情況下 grep 會展示匹配的那一行,如果想查看上下文,可以使用 -A-B-C 這三個參數:

  1. -A 3:展示匹配行以及後面的 3 行

  2. -B 3:展示匹配行以及前面的 3 行

  3. -C 3:展示匹配行以及前後的 3 行,等價於 -A 3 -B 3

另外一些常用的選項包括 -v,表示只顯示那些不匹配的行,-o 表示只顯示匹配的部分,-q 表示不輸出內容,通常與 if 連用。

xargs

在前面的章節中我們介紹過,可以通過管道將多個命令串聯起來,前提是管道後面的命令要支持從標準輸入中讀取數據,比如前文的 grep 命令。

然而有些命令並不支持從標準輸入中讀取,比如這樣寫是無效的:

echo 'file_name' | rm

此時我們可以藉助 xargs 命令:

echo "a" | xargs rm

這條命令的原理是,xargs 會把換行符、空格、製表符、EOF等符號做爲分隔符,把輸入的內容切分爲一個數組,並把數組中每一個元素作爲參數,放到後面的命令中執行,用僞代碼來寫就是:

for arg in read_input; do
rm arg
done

很常見的一個坑就是,如果文件名帶有空格,比如 hello world 就會被 xargs 截斷爲兩個參數,顯然不符合預期。不過一般對內容或者文件進行過濾時,我們都會使用 grepfind,這兩個命令都有辦法配合 xargs

ls | grep 'a' | tr "\n" "\0" | xargs -0 rm

grep 的話會繁瑣一些,需要用 tr 命令把換行符轉換成特殊字符 \0,再利用 xargs-0 參數,根據文檔所述,這個參數會把分隔符指定爲 -0,從而避免了文件名中含有空格的影響。

find 也是類似的原理:

find . -print0 | xargs -0 rm

只不過它自帶了 -print0選項,寫法更簡單。

sed

sed 誕生於 1977 年,已經 41 歲了,這麼一位叔叔級別的命令至今還活躍在各種 Shell 腳本中,由此可見它是多麼重要。

Mac 自帶的時 BSD 版本的 sed,因爲功能較弱,我不推薦使用,建議使用 gsed,如無特殊說明,下文的介紹都是針對 gsed的。

brew install coreutils
which gsed
# /usr/local/bin/gsed

sedgrep 的用法類似,都是 sed pattern file 或者 echo 'xx' | sed pattern,也就是說第二個參數可以是文件,也可以從標準輸入流中讀取。

最標準的用法是進行文本替換(也可以用 tr 命令實現):

echo "a b\nc d"
# a b
# c d
echo "a b\nc d" | gsed 's/a/aa/g'
# aa b
# c d

有時候我們可能不止使用一次 sed,此時可以用 -e 參數把多個命令串聯起來:

echo "a b\nc d" | gsed -e 's/a/aa/g' -e 's/b/bb/g'

gsed 中,還可以使用 Shell 裏定義的變量:

old=a
new=aa
echo "a b\nc d" | gsed "s/$old/$new/g"

我推薦用 gsed 是因爲它有一個 -i 選項,可以對文件進行原地修改:

gsed -i 's/a/aa/g' file

sed 最核心的部分在於這裏的 s/a/aa/g,它由若干個斜槓組成(其實也不一定要用斜槓,只要保持一致就行)。這裏的 s 表示替換,a 表示待匹配的內容,支持正則,aa 表示替換後的內容,g 表示全部替換,更多的用法有:

  1. 1,3s/a/aa/g:只替換第一到三行中的內容

  2. s/a/aa/1:只替換每行第一個 a

  3. s/a/aa/2g:每行前兩個 a 不替換,從第三個開始替換

  4. 2i sss:在第二行前面追加一行,內容爲 sss

  5. 1a sss:在第一行後面追加一行,內容爲 sss,等價於 2i sss

  6. /a/a sss:遇到有字母 a 的行,就在後面追加一行 sss

  7. 1c sss:把第一行替換爲 sss

  8. 2d:刪除第二行

這些用法雖然看起來複雜,但是和 vim 一樣,每個部分就幾種寫法,然後自行排列組合即可。

gsed 在默認情況下,會把輸入的每一行都輸出一遍,它有一個常用的選項是 -n,表示不輸出任何一行。通常與 p 命令合用,這個命令可以打印匹配的行,類似於 grep 的效果。

awk

awk 是和 sed 同時代的命令,並稱爲文本處理兩大神器。個人認爲 sed 的強大之處在於文本匹配後的處理,而 awk 則更適合文本的結構化處理。

這裏以獲取 ip 地址的命令來介紹下:

ifconfig | sed -n -e '/127.0.0.1/d' -e '/inet /p' | awk '{print $2}'

這裏 awk 的用法其實很簡單,就是打印第二列。awk 的核心在於內建的變量:

$0

當前記錄(這個變量中存放着整個行的內容)

$1~$n

當前記錄的第n個字段,字段間由FS分隔

FS

輸入字段分隔符 默認是空格或Tab

NF

當前記錄中的字段個數,就是有多少列

NR

已經讀出的記錄數,就是行號,從1開始,如果有多個文件話,這個值也是不斷累加中。

FNR

當前記錄數,與NR不同的是,這個值會是各個文件自己的行號

RS

輸入的記錄分隔符, 默認爲換行符

awk 一個很常見的用法是 -f 參數,可以指定輸入字段的分隔符:

echo "a;b;c" | awk -F';' '{print $2}'

其實理論上來說,awksed 還要強大,因爲它是一個圖靈完備的語言,支持 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命令的一個,可以把管道輸入的內容轉化爲其參數要操作的文件。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章