shell的三劍客
- grep用來文本搜索,支持正則表達(dá)式
- awk用來數(shù)據(jù)切片
- sed用來修改文件數(shù)據(jù)
grep
grep 是一行一行循環(huán)匹配,匹配到相應(yīng)的值時(shí)會(huì)先輸出,然后換行繼續(xù)匹配再換行直到所有的內(nèi)容都匹配完。
常用參數(shù)
-
-n顯示行號(hào) -
-i忽略大小寫 -
-o精準(zhǔn)匹配' -
E使用擴(kuò)展正則表達(dá)式 -
-v反轉(zhuǎn)查找,即輸出與查找條件不相符合的行 -
-A:后面可加數(shù)字,為 after 的意思,除了列出該行外,后續(xù)的 n 行也列出來 -
-B:后面可加數(shù)字,為 befer 的意思,除了列出該行外,前面的 n 行也列出來 -
--color=auto可將正確的那個(gè)擷取數(shù)據(jù)列出顏色 grep -n -A3 -B2 --color=auto '20428'
實(shí)例1
先創(chuàng)建了一個(gè)test文件輸入以下內(nèi)容
echo "hello from testerhome,this is for grep test" > test
通過grep匹配test文件中的hello from testerhome
[tmp]$ grep "hello from testerhome" test
輸出:hello from testerhome,this is for grep test
# 加上-o精準(zhǔn)匹配
grep -o "hello from testerhome" test
輸出:test:hello from testerhome
# 不加-o會(huì)把包含匹配信息所在行中的所有內(nèi)容輸出(不在一行的不會(huì)輸出)所以內(nèi)
# 加-o只會(huì)輸出匹配的信息,不會(huì)輸出其他多余的信息
實(shí)例2,管道匹配
# 匹配 c
[tmp]$ echo abcd | grep -o c # 將管道前面的輸出內(nèi)容 作為后面的輸入內(nèi)容
輸出:c
實(shí)例3,匹配網(wǎng)頁內(nèi)容
# 通過百度搜索關(guān)鍵字,然后通過正則匹配出搜索結(jié)果約xxx的內(nèi)容,關(guān)鍵字以文件的形式作為參數(shù)循環(huán)傳入
1. 創(chuàng)建文件 vim baidu.keywrod
2. 輸入關(guān)鍵字
python
ios
shell
3. 輸出命令
while read k; do echo $k; curl -s http://www.baidu.com/s?wd=$k; done < baidu.keyword
| grep "結(jié)果約[0-9,]*"
執(zhí)行結(jié)果

分析結(jié)果
- while read k; do echo $k; 循環(huán)讀取內(nèi)容,這里通過重定向輸入到baidu.keywrod這個(gè)文件,去讀取文件中的內(nèi)容
- curl -s http://www.baidu.com/s?wd=$k 這個(gè)是請(qǐng)求百度搜索地址,將奪取的內(nèi)容作為參數(shù)傳給wd 。 -s 會(huì)忽略掉一些curl請(qǐng)求的相關(guān)信息
- | grep "結(jié)果約[0-9,]*" 通過管道命令 后面的gerp+正則匹配得到想要的結(jié)果
實(shí)例4,查看/etc/xxx.conf文件中除了以"#"開頭的行(一般為注釋)和空行以外的所有內(nèi)容
[tmp]$ grep -v "^#" /etc/xxx.conf | grep -v "^#"
awk
awk 可以處理后續(xù)接的文件,也可以讀取來自前個(gè)指令的標(biāo)準(zhǔn)輸出,比較傾向于一行當(dāng)中分成數(shù)個(gè)“字段”來處理。
一般格式
awk '條件類型1{動(dòng)作1} 條件類型2{動(dòng)作2} ...' filename
- awk 'BEGIN{}END{}' 開始和結(jié)束
- awk '/Running/' 正則匹配
- awk '/aa/,/bb/' 區(qū)間選擇
- awk '$2~/xxx/' 字段匹配
- awk 'NR==2' 取第二行
- awk 'NR>1' 去掉第一行
腳本格式
awk
'BEGIN{
初始化語句
}
{
pattern{actions};
pattern{actions};
...
}
END{
讀取所有輸入行后執(zhí)行語句
}'
- 如果BEGIN區(qū)塊存在,awk首先執(zhí)行它里面包含的動(dòng)作指令。
- 當(dāng)awk讀完所有的輸入行后,如果存在END區(qū)域,執(zhí)行END區(qū)域的指令。
- pattern(條件)可以是以下兩種類型:
- 正則表達(dá)式:
/正則表達(dá)式/ - 布爾表達(dá)式:表達(dá)式成立,觸發(fā)相應(yīng)的actions執(zhí)行,如 5>3{print}
- 正則表達(dá)式:
- actions(動(dòng)作)是由許多awk指令構(gòu)成
- awk的I/O指令有print、printf()、getline等
- awk的流程控制指令有if...else...、 while() {...}等
- 所有 awk 的動(dòng)作,亦即在 {} 內(nèi)的動(dòng)作,如果有需要多個(gè)指令輔助時(shí),可利用分號(hào)“;”間隔, 或者直接以[Enter] 按鍵來隔開每個(gè)指令
- 與 bash shell 的變量不同,在 awk 當(dāng)中,變量可以直接使用,不需加上 $ 符號(hào)。
- awk 后面接兩個(gè)
單引號(hào)并加上大括號(hào){}來設(shè)置想要對(duì)數(shù)據(jù)進(jìn)行的處理動(dòng)作。 - awk 主要是處理“每一行的字段內(nèi)的數(shù)據(jù)”,而默認(rèn)的“字段的分隔符號(hào)為 "空白鍵" 或 "[tab]鍵" ”!
也就是說,比如有這樣一行內(nèi)容“hello shell”, awk就會(huì)當(dāng)成是兩列$1取第一列的數(shù)據(jù),$2取第二列的數(shù)據(jù)。
實(shí)例
- 打印1~10的自然數(shù)
[tem]$ awk 'BEGIN{while(++x<=10) print x}'
1
2
3
4
5
6
7
8
9
10
- 取出帳號(hào)與登陸者的 IP ,且?guī)ぬ?hào)與 IP 之間以 [tab] 隔開
# 查看登陸者ip
[tmp]$ last -n 5
177**250 pts/26 182.**.120.43 Mon Jun 24 16:47 still logged in
000**481 pts/20 114.**2.105.123 Mon Jun 24 16:38 still logged in
504**406 pts/29 122.**2.15.42 Mon Jun 24 16:24 still logged in
171**144 pts/16 101.**4.84.70 Mon Jun 24 15:55 still logged in
101**869 pts/8 123.**0.4.245 Mon Jun 24 15:55 still logged in
# 取出帳號(hào)與登陸者的 IP ,且?guī)ぬ?hào)與 IP 之間以 [tab] 隔開
[tmp]$ last -n 5 | awk '{print $1 "\t" $3}'
000**481 114.**2.105.123
504**406 122.**2.15.42
171**144 101.**4.84.70
101**869 123.**0.4.245
298**968 202.**5.145.242
在 awk 的括號(hào)內(nèi),每一行的每個(gè)字段都是有變量名稱的,那就是 $1, $2... 等變量名稱。以上面的例子來說,000**481 是 $1 ,因?yàn)樗堑谝粰诼?!至?14.**2.105.123 是第三欄, 所以他就是$3 啦!后面此類推,$NF代表最后一個(gè)字段。還有個(gè)變量!那就是 $0 ,$0 代表“一整列數(shù)據(jù)”的意思。
-
整個(gè) awk 的處理流程是:
- 讀入第一行,并將第一行的數(shù)據(jù)填入 $0, $1, $2.... 等變量當(dāng)中;
- 依據(jù) "條件類型" 的限制,判斷是否需要進(jìn)行后面的 "動(dòng)作";
- 做完所有的動(dòng)作與條件類型;
- 若還有后續(xù)的“行”的數(shù)據(jù),則重復(fù)上面 1~3 的步驟,直到所有的數(shù)據(jù)都讀完為止
awk內(nèi)置變量
| 變量名稱 | 代表意義 |
|---|---|
| NF | 每一行 ($0) 擁有的字段總數(shù) |
| NR | 目前 awk 所處理的是“第幾行”數(shù)據(jù) |
| FS | 目前字段的分隔字符,默認(rèn)是空白鍵 |
| RS | 行分隔符,默認(rèn)是換行 |
| FNR | 統(tǒng)計(jì)awk讀取過的總行數(shù) |
| OFS | 輸出數(shù)據(jù)的字段分隔符 |
| ORS | 輸出數(shù)據(jù)的行分隔符 |
| FILENAME | 當(dāng)前輸入文件名 |
我們繼續(xù)以上面 last -n 5 的例子來做說明,如果我想要:
- 列出每一行的帳號(hào)(就是 $1);
- 列出目前處理的行數(shù)(就是 awk 內(nèi)的 NR 變量)
- 并且說明,該行有多少字段(就是 awk 內(nèi)的 NF 變量)
[tmp]$ last -n 5| awk '{print $1 "\t 當(dāng)前行數(shù): " NR "\t 當(dāng)前行總段數(shù) " NF}'
02**2304 當(dāng)前行數(shù): 1 當(dāng)前行總段數(shù) 10
72**2968 當(dāng)前行數(shù): 2 當(dāng)前行總段數(shù) 10
95**6181 當(dāng)前行數(shù): 3 當(dāng)前行總段數(shù) 10
17**8250 當(dāng)前行數(shù): 4 當(dāng)前行總段數(shù) 10
00**6481 當(dāng)前行數(shù): 5 當(dāng)前行總段數(shù) 10
實(shí)例2
- 在 /etc/passwd 當(dāng)中是以冒號(hào) ":" 來作為字段的分隔, 該文件中第一字段為帳號(hào),第三字段則是 UID。那假設(shè)我要查閱,第三欄小于 10 以下的數(shù)據(jù),并且僅列出帳號(hào)與第三欄。
[tmp]$ cat /etc/passwd | awk '{FS=":"} $3 < 10 {print $1 "\t " $3}'
root:x:0:0:root:/root:/bin/bash
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
- 找出nginx日志文件中某個(gè)時(shí)間段的日志
cat access.log | awk '$4 >="[28/June/2019:00:00:00" && $4 <="[28/June/2019:23:59:59"{print $0}' > 2019/06/28-access.log
sed
sed 本身也是一個(gè)管道命令,可以分析 standard input ,而且 sed 還可以將數(shù)據(jù)進(jìn)行取代、刪除、新增、截取特定行等等的功能
格式
sed [-nefr] [動(dòng)作]
選項(xiàng)與參數(shù):
-n :使用安靜(silent)模式。在一般 sed 的用法中,所有來自 STDIN 的數(shù)據(jù)一般都會(huì)被列出到屏幕上。但如果加上 -n 參數(shù)后,則只有經(jīng)過 sed 特殊處理的那一行(或者動(dòng)作)才會(huì)被列出來。
-e :直接在命令行界面上進(jìn)行 sed 的動(dòng)作編輯;
-f :直接將 sed 的動(dòng)作寫在一個(gè)文件內(nèi), -f filename 則可以執(zhí)行 filename 內(nèi)的 sed 動(dòng)作;
-r :sed 的動(dòng)作支持的是延伸型正則表達(dá)式的語法。(默認(rèn)是基礎(chǔ)正則表達(dá)式語法)
-i :直接修改讀取的文件內(nèi)容,而不是由屏幕輸出。
動(dòng)作說明: [n1[,n2]]function
n1, n2 :不見得會(huì)存在,一般代表“選擇進(jìn)行動(dòng)作的行數(shù)”,舉例來說,如果我的動(dòng)作是需要在 10 到 20 行之間進(jìn)行的,則“ 10,20[動(dòng)作行為] ”
function 有下面這些咚咚:
a :新增, a 的后面可以接字串,而這些字串會(huì)在新的一行出現(xiàn)(目前的下一行)~
c :取代, c 的后面可以接字串,這些字串可以取代 n1,n2 之間的行!
d :刪除,因?yàn)槭莿h除啊,所以 d 后面通常不接任何咚咚;
i:插入, i 的后面可以接字串,而這些字串會(huì)在新的一行出現(xiàn)(目前的上一行);
p :打印,亦即將某個(gè)選擇的數(shù)據(jù)印出。通常 p 會(huì)與參數(shù) sed -n 一起運(yùn)行~
s :取代,可以取代匹配到的值!通常這個(gè) s 的動(dòng)作可以搭配正則表達(dá)式!
例如 1,20s/old/new/g就是啦!
實(shí)例1
# 將 /etc/passwd 的內(nèi)容列出并且打印行號(hào),同時(shí),請(qǐng)將第 2~5 行刪除!
[tem]$ nl /etc/passwd | sed '2,5d'
1 root:x:0:0:root:/root:/bin/bash
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
# 那個(gè) d 就是刪除!因?yàn)?2-5 行給他刪除了,所以顯示的數(shù)據(jù)就沒有 2-5 行了
# 在第二行后(亦即是加在第三行)加上“hello shell”字樣!
[tem]$ nl /etc/passwd | sed '2a hello shell'
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
hello shell
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 如果要新增多行每一行之間都必須要以反斜線“ \ ”來進(jìn)行新行的增加
[tem]$ nl /etc/passwd | sed '2a hello shell \
> hello world'
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
hello shell
hello world
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 取出 11~20 行
[tem]$ nl /etc/passwd | sed -n '11,20p'
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-bus-proxy:x:999:998:systemd Bus Proxy:/:/sbin/nologin
15 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
16 dbus:x:81:81:System message bus:/:/sbin/nologin
17 polkitd:x:998:997:User for polkitd:/:/sbin/nologin
18 tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
19 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
20 postfix:x:89:89::/var/spool/postfix:/sbin/nologin
請(qǐng)注意:
- sed 后面接的動(dòng)作,請(qǐng)務(wù)必以
''兩個(gè)單引號(hào)括住喔! - 如果有多個(gè)動(dòng)作 使用 -e 分隔 如,
nl /etc/passwd | sed -n -e '1,10p' -e '2,5d' -e '6i hello'
部分?jǐn)?shù)據(jù)的搜索并取代
sed 's/要被取代的字串/新的字串/g'
實(shí)例2
取出ip地址
#先觀察原始訊息,利用 /sbin/ifconfig eth0 查詢 IP
[tem]$ /sbin/ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.147.8 netmask 255.255.240.0 broadcast 172.19.159.255
ether 00:16:3e:04:1c:75 txqueuelen 1000 (Ethernet)
RX packets 101946929 bytes 30358246998 (28.2 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 84401729 bytes 45138874295 (42.0 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# 取出172.19.147.8
[tem]$ /sbin/ifconfig eth0 | grep "inet" | sed 's/.*inet//g' | sed 's/ *netmask.*//g'
172.19.147.8
說明:
1. 先用grep過濾出包含inet的行
2. 然后使用sed干掉ip地址前面的空字符串以及inet
3. 最后把 ip地址后面字符串也干掉
補(bǔ)充正則
字符匹配:
.任意單個(gè)字符
[]指定范圍的字符
[^]不在指定范圍的字符
次數(shù)匹配:
* 匹配前面字符0次或者任意次
? 0或1次
+ 1次或多次
{m} 匹配m次
{m,n} 至少m,至多n次
位置:
^ 行首
$ 行尾
^$ 空行
分組:
() 表示一個(gè)整體
egrep ‘r(oo)|(at)o‘ 1.txt 匹配roo或者ato
或者:
|
a|b a或b
C|cat C或cat
(C|c)at Cat或cat
補(bǔ)充一個(gè)好用的命令xargs
- xargs 是一個(gè)強(qiáng)有力的命令,它能夠捕獲一個(gè)命令的輸出,然后傳遞給另外一個(gè)命令。
- xargs 也可以將單行或多行文本輸入轉(zhuǎn)換為其他格式,例如多行變單行,單行變多行。
- xargs 默認(rèn)的命令是 echo,這意味著通過管道傳遞給 xargs 的輸入將會(huì)包含換行和空白,不過通過 xargs 的處理,換行和空白將被空格取代。
實(shí)例
定義一個(gè)測試文件,內(nèi)有多行文本數(shù)據(jù):
$ cat test.txt
a b c d e f g
h i j k l m n
o p q
r s t
u v w x y z
多行輸入單行輸出:
$ cat test.txt | xargs
a b c d e f g h i j k l m n o p q r s t u v w x y z
-n 選項(xiàng)單行轉(zhuǎn)多行
$ echo "a b c d e f g h i j k l m n o p q r s t u v w x y z" | xargs -n2
a b
c d
e f
g h
i j
k l
m n
o p
q r
s t
u v
w x
y z
# -n num 后面加次數(shù),表示命令在執(zhí)行的時(shí)候一次要使用幾個(gè)參數(shù)的意思,默認(rèn)是用所有的。
-d 選項(xiàng)可以自定義一個(gè)分隔符
$ echo "abcXabcXabcX" | xargs -dX
abc abc abc
# 結(jié)合-n使用
echo "abcXabcXabcX" | xargs -dX -n1
abc
abc
abc
補(bǔ)充Perl正則
$ echo "Hello, my name is aming."|grep -oP '(?<=Hello, ).*(?= aming.)'
my name is
說明
這意思是,-P 可以讓grep使用perl的正則表達(dá)式語法,因?yàn)閜erl的正則更加多元化,能實(shí)現(xiàn)更加復(fù)雜的場景。