三年前,作為面試官,我負責招聘unix系統管理員。那次來競聘該崗位的共有8個人,其中兩人是技術一流的自由職業者。我認為系統管理員沒必要把所有的配置方法熟記於心,需要某個軟件的時候,只要你想把它玩轉並且玩得更酷,多讀些文章,你便會自然而然地熟悉它的各種用法與配置。於是,我讓這些應聘者解決下面兩個問題:
讓我吃驚的是,8位應聘者中沒有一個人能解決上述問題,其中兩人竟對grep命令一無所知。
介於此,我們就好好說說grep。
首先,以下所有的操作都是基於grep 2.5.1-FreeBSD:
# grep --version | grep grep grep (GNU grep) 2.5.1-FreeBSD
有必要先交待下grep版本,因為某些用法只限定於特定的版本:
# man grep | grep -iB 2 freebsd -P, --perl-regexp Interpret PATTERN as a Perl regular expression. This option is not supported in FreeBSD.
好了,言歸正傳,我們經常會這樣grep文件:
root@nm3:/ # cat /var/run/dmesg.boot | grep CPU: CPU: Intel Core(TM)2 Quad CPU Q9550 @ 2.83GHz (2833.07-MHz K8-class CPU)
還可以這樣做:
root@nm3:/ # grep CPU: /var/run/dmesg.boot CPU: Intel Core(TM)2 Quad CPU Q9550 @ 2.83GHz (2833.07-MHz K8-class CPU)
這樣也是可以的(雖然我很討厭這種操作方式):
root@nm3:/ # </var/run/dmesg.boot grep CPU: CPU: Intel Core(TM)2 Quad CPU Q9550 @ 2.83GHz (2833.07-MHz K8-class CPU)
你肯定會遇到這樣的場景:統計文件中帶有某些關鍵字的行出現的次數。grep+wc可以幫到你:
root@nm3:/ # grep WARNING /var/run/dmesg.boot | wc -l 3
條條大路通羅馬,下面是另一條路:
root@nm3:/ # grep WARNING /var/run/dmesg.boot -c 3
下面我們新建一個測試用的文檔:
root@nm3:/ # grep ".*" test.txt one two three seven eight one eight three thirteen fourteen fifteen sixteen seventeen eighteen seven sixteen seventeen eighteen twenty seven one 504 one one 503 one one 504 one one 504 one #comment UP twentyseven #comment down twenty1 twenty3 twenty5 twenty7
繼續grep的搜索之旅。
-w選項指定要搜索的單詞:
root@nm3:/ # grep -w 'seven' test.txt seven eight one eight three sixteen seventeen eighteen seven twenty seven
如果想搜以特定字符開頭(結尾)的單詞,可以這樣:
root@nm3:/ # grep '<seven' test.txt seven eight one eight three sixteen seventeen eighteen seven sixteen seventeen eighteen twenty seven root@nm3:/ # grep 'seven>' test.txt seven eight one eight three sixteen seventeen eighteen seven twenty seven twentyseven
如果想搜以特定字符開頭(結尾)的行,可以這樣:
root@nm3:/ # grep '^seven' test.txt seven eight one eight three root@nm3:/ # grep 'seven$' test.txt sixteen seventeen eighteen seven twenty seven twentyseven root@nm3:/ #
想要顯示目標行的上下文嗎?
root@nm3:/ # grep -C 1 twentyseven test.txt #comment UP twentyseven #comment down
到底是顯示上文還是下文?
root@nm3:/ # grep -A 1 twentyseven test.txt twentyseven #comment down root@nm3:/ # grep -B 1 twentyseven test.txt #comment UP twentyseven
我們還可以這樣玩grep:
root@nm3:/ # grep "twenty[1-4]" test.txt twenty1 twenty3
或者取非:
root@nm3:/ # grep "twenty[^1-4]" test.txt twenty seven twentyseven twenty5 twenty7
grep是個強大的指令,除上述列舉的之外,它還支持許多限定符、通配符以及正則表達式。下面是一些例子:
root@nm3:/ # cat /etc/resolv.conf #options edns0 #nameserver 127.0.0.1 nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4
只獲取IP地址相關的行:
root@nm3:/ # grep -E "[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}" /etc/resolv.conf #nameserver 127.0.0.1 nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4
上面的方法可行,但下面這種方法更好:
root@nm3:/ # grep -E 'b[0-9]{1,3}(.[0-9]{1,3}){3}b' /etc/resolv.conf #nameserver 127.0.0.1 nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4
希望去掉注釋行?
root@nm3:/ # grep -E 'b[0-9]{1,3}(.[0-9]{1,3}){3}b' /etc/resolv.conf | grep -v '#' nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4
只要IP:
root@nm3:/ # grep -oE 'b[0-9]{1,3}(.[0-9]{1,3}){3}b' /etc/resolv.conf | grep -v '#' 127.0.0.1 8.8.8.8 77.88.8.8 8.8.4.4
哎呀,被注釋掉的127.0.0.1又回來了,這是指令執行順序不當導致的,怎麼破?
root@nm3:/ # grep -v '#' /etc/resolv.conf | grep -oE 'b[0-9]{1,3}(.[0-9]{1,3}){3}b' 8.8.8.8 77.88.8.8 8.8.4.4
下面看下-v(反向查找)選項的使用。
假設要執行指令“ps –afx | grep ttyv ”:
root@nm3:/ # ps -afx | grep ttyv 1269 v1 Is+ 0:00.00 /usr/libexec/getty Pc ttyv1 1270 v2 Is+ 0:00.00 /usr/libexec/getty Pc ttyv2 1271 v3 Is+ 0:00.00 /usr/libexec/getty Pc ttyv3 1272 v4 Is+ 0:00.00 /usr/libexec/getty Pc ttyv4 1273 v5 Is+ 0:00.00 /usr/libexec/getty Pc ttyv5 1274 v6 Is+ 0:00.00 /usr/libexec/getty Pc ttyv6 1275 v7 Is+ 0:00.00 /usr/libexec/getty Pc ttyv7 48798 2 S+ 0:00.00 grep ttyv
OK,但是我們不需要“48798 2 S+ 0:00.00 grep ttyv”一行,使用-v:
root@nm3:/ # ps -afx | grep ttyv | grep -v grep 1269 v1 Is+ 0:00.00 /usr/libexec/getty Pc ttyv1 1270 v2 Is+ 0:00.00 /usr/libexec/getty Pc ttyv2 1271 v3 Is+ 0:00.00 /usr/libexec/getty Pc ttyv3 1272 v4 Is+ 0:00.00 /usr/libexec/getty Pc ttyv4 1273 v5 Is+ 0:00.00 /usr/libexec/getty Pc ttyv5 1274 v6 Is+ 0:00.00 /usr/libexec/getty Pc ttyv6 1275 v7 Is+ 0:00.00 /usr/libexec/getty Pc ttyv7
看著不爽?現在呢?
root@nm3:/ # ps -afx | grep "[t]tyv" 1269 v1 Is+ 0:00.00 /usr/libexec/getty Pc ttyv1 1270 v2 Is+ 0:00.00 /usr/libexec/getty Pc ttyv2 1271 v3 Is+ 0:00.00 /usr/libexec/getty Pc ttyv3 1272 v4 Is+ 0:00.00 /usr/libexec/getty Pc ttyv4 1273 v5 Is+ 0:00.00 /usr/libexec/getty Pc ttyv5 1274 v6 Is+ 0:00.00 /usr/libexec/getty Pc ttyv6 1275 v7 Is+ 0:00.00 /usr/libexec/getty Pc ttyv7
別忘了| (或)符號:
root@nm3:/ # vmstat -z | grep -E "(sock|ITEM)" ITEM SIZE LIMIT USED FREE REQ FAIL SLEEP socket: 696, 130295, 30, 65, 43764, 0, 0
殊途同歸:
root@nm3:/ # vmstat -z | grep "sock|ITEM" ITEM SIZE LIMIT USED FREE REQ FAIL SLEEP socket: 696, 130295, 30, 65, 43825, 0, 0
許多人都會在grep中用正則表達式,但你仍會忘了用POSIX字符集,即便它們也非常有用。
POSIX:
[:alpha:] Any alphabetical character, regardless of case [:digit:] Any numerical character [:alnum:] Any alphabetical or numerical character [:blank:] Space or tab characters [:xdigit:] Hexadecimal characters; any number or A–F or a–f [:punct:] Any punctuation symbol [:print:] Any printable character (not control characters) [:space:] Any whitespace character [:graph:] Exclude whitespace characters [:upper:] Any uppercase letter [:lower:] Any lowercase letter [:cntrl:] Control characters
找有大寫字母的行:
root@nm3:/ # grep "[[:upper:]]" test.txt #comment UP
搜索結構不夠醒目?高亮顯示:
更多的grep小竅門。第一個稍顯專業,我已經15年沒用過了。
選擇包含six,seven或者eight的行,很簡單:
root@nm3:/ # grep -E "(six|seven|eight)" test.txt seven eight one eight three sixteen seventeen eighteen seven sixteen seventeen eighteen twenty seven twentyseven
那麼現在只選擇包含six,seven或者eight若干次的行。這種用法叫回溯引用:
root@nm3:/ # grep -E "(six|seven|eight).*1" test.txt seven eight one eight three sixteen seventeen eighteen seven
第二個竅門,這個更有用一些。打印504前後有tab的行(如果PCRE能夠支持這個特性就好了)。
POSIX字符集在此失效了:
root@nm3:/ # grep "[[:blank:]]504[[:blank:]]" test.txt one 504 one one 504 one one 504 one
[CTRL+V][TAB]生效:
root@nm3:/ # grep " 504 " test.txt one 504 one
我漏講什麼了嗎?grep具備遞歸搜索文件/目錄功能。如果我們想在源碼目錄中搜索允許Intel使用外部SFPs的代碼,但是又沒清楚完整地記著函數名allow_unsupported_stp和unsupported_allow_sfp。腫麼辦?這正是grep的菜:
root@nm3:/ # grep -rni allow /usr/src/sys/dev/ | grep unsupp
/usr/src/sys/dev/ixgbe/README:75:of unsupported modules by setting the static variable 'allow_unsupported_sfp'
/usr/src/sys/dev/ixgbe/ixgbe.c:322:static int allow_unsupported_sfp = TRUE;
/usr/src/sys/dev/ixgbe/ixgbe.c:323:TUNABLE_INT("hw.ixgbe.unsupported_sfp", &allow_unsupported_sfp);
/usr/src/sys/dev/ixgbe/ixgbe.c:542: hw->allow_unsupported_sfp = allow_unsupported_sfp;
/usr/src/sys/dev/ixgbe/ixgbe_type.h:3249: bool allow_unsupported_sfp;
/usr/src/sys/dev/ixgbe/ixgbe_phy.c:1228: if (hw->allow_unsupported_sfp == TRUE) {
希望你還沒暈,因為這些grep用法只是grep的冰山一角呢!
最後祝大家 Happy grepping!