awk、sed和grep號稱文本處理三劍客。
(1)awk的基本用法
a)awk默認使用空白(一個或者多個空格、一個或者多個\t,一個或者多個空格和\t的組合)作爲分隔符。
例如:文件testfile
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 60 67 60
jj 201907004 59 57 58
aa 201907005 23 34 12
使用awk獲取文件內容的第一列,結果如下:
[root@172-0-10-222 shell-test]# cat testfile | awk '{print $1}'
ll
kk
hh
jj
aa
b)假如有多個空格和\t,文件如下:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 60 67 60
jj 201907004 59 57 58
aa 201907005 23 34 12
使用awk默認分割效果跟上面一樣:
[root@172-0-10-222 shell-test]# cat testfile | awk '{print $1}'
ll
kk
hh
jj
aa
c)awk還可以使用多個不同字符進行文本的分割,文件如下:
[root@172-0-10-222 shell-test]# cat testfile
ll: 201907001 80 97 70
kk: 201907002 90 97 90
hh 201907003 60 67 60
jj 201907004 59 57 58
aa 201907005 23 34 12
使用一個或多個空格,一個或多個冒號,一個或多個冒號和空格的組合進行文本的分割:
[root@172-0-10-222 shell-test]# cat testfile | awk -F '[: ]+' '{print $1}'
ll
kk
hh
jj
aa
案例:獲取本機ip
查看ip信息如下:
[root@172-0-10-222 shell-test]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:50:56:87:37:a9 brd ff:ff:ff:ff:ff:ff
inet 172.0.10.222/16 brd 172.0.255.255 scope global noprefixroute ens32
valid_lft forever preferred_lft forever
inet6 fe80::2dea:8641:fee:950f/64 scope link noprefixroute
valid_lft forever preferred_lft forever
使用awk獲取ip:
[root@172-0-10-222 shell-test]# ip a | grep 'scope global' | awk -F '[ /]+' '{print $3}'
172.0.10.222
(2)awk的內置變量NF
NF(number of field)表示當前行被分割後的字段(列)數。
測試文件如下:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 67 60
jj 201907004 59 57 58
aa 201907005 12
使用NF獲取每一行被分割後的字段數
[root@172-0-10-222 shell-test]# cat testfile | awk '{print NF}'
5
5
4
5
3
輸出每行最後一列的數據
[root@172-0-10-222 shell-test]# cat testfile | awk '{print $NF}'
70
90
60
58
12
輸出每行倒數第二列的數據
[root@172-0-10-222 shell-test]# cat testfile | awk '{print $(NF-1)}'
97
97
67
57
201907005
(3)awk的內置變量NR和FNR
NR(number of record)表示行號,FNR可以將多個文件的行號獨立排序。
測試文件:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 67 60
jj 201907004 59 57 58
aa 201907005 12
使用NR打印行號:
[root@172-0-10-222 shell-test]# cat testfile | awk '{print NR}'
1
2
3
4
5
分別使用NR和FNR打印多個文件的整體行號和獨立行號:
測試文件如下:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 67 60
jj 201907004 59 57 58
aa 201907005 12
[root@172-0-10-222 shell-test]# cat testfile2
ll 201907001 80 97 70
hh 201907003 67 60
aa 201907005 12
NR輸出多個文件的整體行號
[root@172-0-10-222 shell-test]# awk '{print NR}' testfile testfile2
1
2
3
4
5
6
7
8
FNR輸出多個文件的獨立行號
[root@172-0-10-222 shell-test]# awk '{print FNR}' testfile testfile2
1
2
3
4
5
1
2
3
一般情況下,FNR不常用。
NR的常用用法:輸出指定的行。
例如:輸出testfile的第三行
[root@172-0-10-222 shell-test]# awk 'NR==3{print}' testfile
hh 201907003 67 60
輸出testfile的第2行到第四行
[root@172-0-10-222 shell-test]# awk 'NR>=2&&NR<=4{print}' testfile
kk 201907002 90 97 90
hh 201907003 67 60
jj 201907004 59 57 58
注意:awk的執行過程是pattern{action}方式,就是單引號中的輸出方式是滿足條件後輸出。比如上面的'NR>=2&&NR<=4{print}',表示行號是2到4的行輸出。可以使用這種方式,靈活輸出滿足條件的任意列。
(4)awk中使用BEGIN和END
BEGIN和END分別在awk的正常輸出之前和之後做一些事情。所謂的之後,就是在awk執行完每一行結束後。
測試文件入下:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 67 60
jj 201907004 59 57 58
aa 201907005 12
輸出文件的2-4行,並且在前面加上========begin========,在後面加上========end========
[root@172-0-10-222 shell-test]# awk 'BEGIN{print "========begin========"}NR>=2&&NR<=4{print $0}END{print "========end========"}' testfile
========begin========
kk 201907002 90 97 90
hh 201907003 67 60
jj 201907004 59 57 58
========end========
通常,BEGIN可以用來做變量賦值。
(5)awk中的計算
測試文件:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 67 60 77
jj 201907004 59 57 58
aa 201907005 89 62 63
將每一行的後三列之和放在最後一列
[root@172-0-10-222 shell-test]# cat testfile | awk '{print $0,$3+$4+$5}'
ll 201907001 80 97 70 247
kk 201907002 90 97 90 277
hh 201907003 67 60 77 204
jj 201907004 59 57 58 174
aa 201907005 89 62 63 214
再將每一行的後三列的平均數放在最後一列
[root@172-0-10-222 shell-test]# cat testfile | awk '{sum=$3+$4+$5;print $0,sum,sum/3}'
ll 201907001 80 97 70 247 82.3333
kk 201907002 90 97 90 277 92.3333
hh 201907003 67 60 77 204 68
jj 201907004 59 57 58 174 58
aa 201907005 89 62 63 214 71.3333
後三列,計算每行之和
[root@172-0-10-222 shell-test]# cat testfile | awk '{a+=$3;b+=$4;c+=$5}END{print a,b,c}'
385 373 358
案例:統計/usr/bin/下面的所有進程佔CPU和內存的百分比。
/usr/bin/下面的所有進程的情況:
[root@172-0-10-222 shell-test]# ps -aux | grep /usr/bin/
dbus 643 0.0 0.1 66432 2588 ? Ssl Nov06 0:03 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
root 646 0.0 0.3 99656 6112 ? Ss Nov06 0:00 /usr/bin/VGAuthService -s
root 647 0.1 0.3 298660 6172 ? Ssl Nov06 17:05 /usr/bin/vmtoolsd
root 1013 0.0 0.9 573852 17004 ? Ssl Nov06 2:04 /usr/bin/python -Es /usr/sbin/tuned -l -P
root 10245 0.0 0.0 112704 972 pts/1 S+ 14:08 0:00 grep --color=auto /usr/bin/
第三列和第四列分別是CPU和內存,所以將這兩列的每行相加即可
[root@172-0-10-222 shell-test]# ps -aux | grep /usr/bin/ | awk '{cpu+=$3;mem+=$4}END{print cpu,mem}'
0.1 1.6
(6)awk中if的使用
if判斷,跟之前的pattern{action}模式功能類似。
測試目錄:
[root@172-0-10-222 shell-test]# ll
total 100
-rw-r--r--. 1 root root 0 Oct 21 19:20 4]]
-rw-r--r--. 1 root root 6048 Nov 13 16:19 dataMigration_part1.sh
-rwxr-xr-x. 1 root root 1059 Nov 13 15:10 dataMigration_part2.sh
-rwxr-xr-x. 1 root root 104 Oct 21 19:19 expr_test.sh
-rwxr-xr-x. 1 root root 161 Oct 16 20:22 input_test.sh
drwxr-xr-x. 2 root root 6 Nov 6 14:02 ipconf
-rw-r--r--. 1 root root 160 Nov 6 11:22 ip.txt
-rwxr-xr-x. 1 root root 94 Oct 18 17:36 let_test2.sh
-rwxr-xr-x. 1 root root 229 Oct 18 17:26 let_test.sh
-rwxr-xr-x. 1 root root 1107 Nov 12 11:31 modifyDir.sh
-rw-r--r--. 1 root root 738 Nov 13 09:00 modifyNfsConfiguration.sh
-rw-r--r--. 1 root root 2798 Nov 13 11:59 modifyShareConfiguration.sh
drwxr-xr-x. 2 root root 197 Nov 11 14:15 myfolder
-rw-r--r--. 1 root root 17 Nov 12 20:24 test
-rwxr-xr-x. 1 root root 243 Nov 6 11:05 test_break_continue.sh
-rwxr-xr-x. 1 root root 234 Oct 30 11:23 test_case.sh
-rw-r--r--. 1 root root 76 Oct 16 20:22 test.cnf
-rw-r--r--. 1 root root 115 Nov 14 13:44 testfile
-rw-r--r--. 1 root root 63 Nov 14 10:49 testfile2
-rwxr-xr-x. 1 root root 241 Oct 31 19:34 test_for.sh
-rwxr-xr-x. 1 root root 395 Oct 30 11:17 test_if.sh
-rw-r--r--. 1 root root 84 Nov 13 14:23 test.sh
-rwxr-xr-x. 1 root root 78 Oct 31 19:57 test_while_case.sh
-rwxr-xr-x. 1 root root 367 Nov 6 13:59 test_whilereadline2.sh
-rwxr-xr-x. 1 root root 313 Nov 6 11:38 test_whilereadline.sh
-rwxr-xr-x. 1 root root 217 Oct 31 19:53 test_while.sh
-rwxr-xr-x. 1 root root 254 Oct 31 20:19 test_xunhuan_case.sh
查詢出指定目錄下,大於1K的所有文件。
[root@172-0-10-222 shell-test]# ll | awk '/^-/{if($5>1024){print $0}}'
-rw-r--r--. 1 root root 6048 Nov 13 16:19 dataMigration_part1.sh
-rwxr-xr-x. 1 root root 1059 Nov 13 15:10 dataMigration_part2.sh
-rwxr-xr-x. 1 root root 1107 Nov 12 11:31 modifyDir.sh
-rw-r--r--. 1 root root 2798 Nov 13 11:59 modifyShareConfiguration.sh
(7)awk的for循環
測試文件:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 67 60 77
jj 201907004 59 57 58
aa 201907005 89 62 63
循環輸出按照默認分隔符(空格)分割得到的數據
[root@172-0-10-222 shell-test]# cat testfile | awk '{for(i=1;i<=NF;i++){print $i}}'
ll
201907001
80
97
70
kk
201907002
90
97
90
hh
201907003
67
60
77
jj
201907004
59
57
58
aa
201907005
89
62
63
如果想在上面輸出數據的基礎上,打印成最開始文件的內容格式,可以採用如下做法
[root@172-0-10-222 shell-test]# cat testfile | awk '{for(i=1;i<=NF;i++){printf $i" "}print xxoo}'
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201907003 67 60 77
jj 201907004 59 57 58
aa 201907005 89 62 63
其中,print xxoo表示打印一個空變量,效果就相當於空行。也可以寫print ""或者print " "都行。
(8)awk中使用正則表達式
在awk中使用正則表達式,基本都是用來匹配條件,然後進行打印輸出的。因此,使用正則的位置通常都是在if條件中(或者前面說的patten位置)。
測試文件:
[root@172-0-10-222 shell-test]# cat testfile
ll 201907001 80 97 70
kk 201907002 90 97 90
hh 201908003 67 60 77
jj 201908004 59 57 58
aa 201909005 67 82 63
a)輸出第二行以後的所有行
[root@172-0-10-222 shell-test]# cat testfile | awk 'NR>=2{print $0}'
kk 201907002 90 97 90
hh 201908003 67 60 77
jj 201908004 59 57 58
aa 201909005 67 82 63
或者寫成:cat testfile | awk '{if(NR>=2){print $0}}'
b)使用正則表達式過濾出包含8的行
[root@172-0-10-222 shell-test]# cat testfile | awk '/8/&&NR>=2{print $0}'
hh 201908003 67 60 77
jj 201908004 59 57 58
aa 201909005 67 82 63
或者寫成:cat testfile | awk '/8/{if(NR>=2){print $0}}'
c)對上述結果過濾出第二列包含8的行
[root@172-0-10-222 shell-test]# cat testfile | awk '$2~/8/&&/8/&&NR>=2{print $0}'
hh 201908003 67 60 77
jj 201908004 59 57 58
也可以這樣寫:cat testfile | awk '$2~/8/&&/8/{if(NR>=2){print $0}}'