經(jīng)常讀一些介紹unix命令的文章,但是文章所舉的例子普遍價值不大,這就導(dǎo)致了看完了文章后我們并不知道如何有效地運用這些unix命令。
寫在前面
三年前,作為面試官,我負(fù)責(zé)招聘unix系統(tǒng)管理員。那次來競聘該崗位的共有8個人,其中兩人是技術(shù)***的自由職業(yè)者。我認(rèn)為系統(tǒng)管理員沒必要把所有的配置方法熟記于心,需要某個軟件的時候,只要你想把它玩轉(zhuǎn)并且玩得更酷,多讀些文章,你便會自然而然地熟悉它的各種用法與配置。于是,我讓這些應(yīng)聘者解決下面兩個問題:
- 創(chuàng)建一個例行任務(wù),它在每個偶數(shù)點(比如2點、12點)和3點執(zhí)行;
- 通過/var/run/dmesg.boot文件打印處理器信息。
讓我吃驚的是,8位應(yīng)聘者中沒有一個人能解決上述問題,其中兩人竟對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.
好了,言歸正傳,我們經(jīng)常會這樣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)
你肯定會遇到這樣的場景:統(tǒng)計文件中帶有某些關(guān)鍵字的行出現(xiàn)的次數(shù)。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
繼續(xù)grep的搜索之旅。
-w選項指定要搜索的單詞:
- root@nm3:/ # grep -w 'seven' test.txt
- seven eight one eight three
- sixteen seventeen eighteen seven
- twenty seven
如果想搜以特定字符開頭(結(jié)尾)的單詞,可以這樣:
- 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
如果想搜以特定字符開頭(結(jié)尾)的行,可以這樣:
- 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:/ #
想要顯示目標(biāo)行的上下文嗎?
- 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是個強(qiáng)大的指令,除上述列舉的之外,它還支持許多限定符、通配符以及正則表達(dá)式。下面是一些例子:
- 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地址相關(guān)的行:
- 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又回來了,這是指令執(zhí)行順序不當(dāng)導(dǎo)致的,怎么破?
- 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(反向查找)選項的使用。
假設(shè)要執(zhí)行指令“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
看著不爽?現(xiàn)在呢?
- 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中用正則表達(dá)式,但你仍會忘了用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
搜索結(jié)構(gòu)不夠醒目?高亮顯示:
更多的grep小竅門。***個稍顯專業(yè),我已經(jīng)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
那么現(xiàn)在只選擇包含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的代碼,但是又沒清楚完整地記著函數(shù)名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_sfpallow_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!