一、初識AWK
AWK
有其他的衍生版本,比如nawk
和gawk
,在Linux發行版裏,默認使用的是gawk
:
# ls -l `which awk`
lrwxrwxrwx. 1 root root 4 5月 9 20:25 /usr/bin/awk -> gawk
二、AWK的工作流程
AWK
是按行處理文本,將行按照某一分隔符進行分割,其中分割之後的第一部分爲$1
,第二部分爲$2
,依次類推,其中$0
表示整行,超過10的要將數字用括號括起來,比如$(10)
AWK
默認的分隔符是空格和tab
,其中操作命令必須寫在{}裏面,語法:
awk '/模式/{print $1}' file //模式支持正則表達式,只對含有該模式的行進行操作
- 可以使用
-F
選項指定分隔符:
awk -F: '/模式/{print $1}' file
三、常見操作符的使用(例子中passwd文件使用/etc/passwd文件的前6行)
常見的操作符有:~
、!~
、==
、!=
、>
、<
、++
、--
等
~
和!~
表示包含和不包含:
# awk -F: '$1~/ro/{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
==
和!=
表示嚴格的等於和不等於:
# awk -F: '$3==1{print $0}' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
# awk -F: '{$3++;print $3}' passwd
1
2
3
4
5
6
- 其他的一些操作符同理,兩個以上的條件需要使用連接符
&&
或||
四、AWK中 BEGIN 和 NED 的用法
BEGIN
中的命令是在AWK讀取文本的第一行之前進行操作:
# awk -F: 'BEGIN{print "用戶名"}{print $1}' passwd
用戶名
root
bin
daemon
adm
lp
sync
END
中的命令是在讀取完最後一行之後進行的操作:
# awk -F: '{print $1}END{print "結束"}' passwd
root
bin
daemon
adm
lp
sync
結束
- BEGIN和END可以同時出現,也可以單獨出現,BEGIN中可以定義變量:
# awk -F: 'BEGIN{a=1}{print a}' passwd
1
1
1
1
1
1
如何沒有預先定義變量的話,在後面也是可以引用的,只不過這個變量的值爲空。引用變量的時候不加$
END
中也可以進行最終的計算:
# awk -F: 'BEGIN{sum=0}{sum+=$3}END{print "所有UID的和爲:"sum}' passwd
所有UID的和爲:15
或者不預先定義變量:
# awk -F: '{sum+=$3}END{print "所有UID的和爲:"sum}' passwd
所有UID的和爲:15
五、AWK中常見內置變量的用法
FS
代表分隔符:
# awk 'BEGIN{FS=":"}{print $1}' passwd
root
bin
daemon
adm
lp
sync
OFS
爲打印輸出的分隔符,默認的是空格:
# awk 'BEGIN{FS=":";OFS="---"}{print $1,$2}' passwd
root---x
bin---x
daemon---x
adm---x
lp---x
sync---x
NF
的值表示將每一行按照FS
指定的分隔符分爲多少段:
# awk 'BEGIN{FS=":"}{print NF}' passwd
7
7
7
7
7
7
如果要打印每一行的最後一段,可以使用以下用法:
# awk 'BEGIN{FS=":"}{print $NF}' passwd
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/sync
- 前面提到過,AWK是將文件按行進行處理,那麼AWK是如何分別每一行的呢?使用的就是
RS
的值,默認爲\n
:
# cat test.txt
1,張三:經理 2,李四:職員 3,王五:老闆
# awk -F[:,] 'BEGIN{RS=" "}{print $2,$3}' test.txt
張三 經理
李四 職員
王五 老闆
ORS
變量的值決定輸出行的分隔符,默認的爲\n
:
# awk 'BEGIN{ORS="---"}{print $0}' passwd
root:x:0:0:root:/root:/bin/bash---bin:x:1:1:bin:/bin:/sbin/nologin---daemon:x:2:2:daemon:/sbin:/sbin/nologin---adm:x:3:4:adm:/var/adm:/sbin/nologin---lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin---sync:x:5:0:sync:/sbin:/bin/sync---
FILENAME
變量的值爲當前操作文件的文件名:
# awk -F: '{print FILENAME}' passwd
passwd
passwd
passwd
passwd
passwd
passwd
NR
變量的值表示當前正在操作的行號:
# awk -F: '{print NR}' passwd
1
2
3
4
5
6
如果這裏將行分隔符RS
賦值爲-
,驗證NR
的值:
# awk 'BEGIN{RS="-"}{print NR}' passwd
1
FNR
也表示當前操作的行號,不同的是,當對一個文件進行操作的時候FNR
和NR
的值是一樣的。當對兩個文件進行操作時,FNR
和NR
的值是不一樣的:
# awk -F: '{print NR,$1,FNR}' passwd shadow
1 root 1
2 bin 2
3 daemon 3
4 adm 4
5 lp 5
6 sync 6
7 root 1
8 bin 2
9 daemon 3
10 adm 4
11 lp 5
12 sync 6
六、判斷和循壞語句
if
語句:
語法,如果語句沒有使用{},則要使用;分隔
if(判斷1){語句1}else if(判斷2){語句2}else{語句3}
if(判斷1)語句1;else if(判斷2)語句2;else語句3
# awk -F: '{if($3==0){print $1"是root"}else if($3>=1000){print $1"是普通用戶"}else{print $1"是系統用戶"}}' passwd
root是root
bin是系統用戶
daemon是系統用戶
adm是系統用戶
lp是系統用戶
sync是系統用戶
for
循壞語句:
awk -F: '{for(i=1;i<=NF-5;i++){print $i}}' passwd
root
x
bin
x
daemon
x
while
循壞語句:
# awk -F: '{print "------------";i=1;while(i<=NF){print $i;i++}}' passwd
------------
root
x
0
0
root
/root
/bin/bash
------------
bin
x
1
1
bin
/bin
/sbin/nologin
------------
daemon
x
2
2
daemon
/sbin
/sbin/nologin
- 循壞控制語句
break
:
break只能用在循環語句中,作用是跳出當前循環,繼續下一循環
# echo -e "1 2 3 4 1\n5 6 7" | awk '{for(i=1;i<=NF;i++){if($i>=2){break}print $i}}'
1
- 循環控制語句
continue
:如果遇到continue,continue後面的語句不再執行,繼續下一個循環
# echo -e "1 2 3 4 1\n5 6 7" | awk '{for(i=1;i<=NF;i++){if($i>=2){continue}print $i}}'
1
1
- 循環控制語句
next
:遇到next,跳出循環,繼續下移循環,在循環語句中next和break語句效果一樣。區別在於,break和continue語句只能用於循環語句,next語句還可以用於其他語句,以下例子用於判斷語句
# echo -e "1 2 3 4 1\n5 6 7 1" | awk '{if($2<=2){next}print $0}'
5 6 7 1
# awk -F: 'NR==FNR{print NR,FILENAME,FNR;next}{print NR,FILENAME,FNR}' passwd shadow
1 passwd 1
2 passwd 2
3 passwd 3
4 shadow 1
5 shadow 2
6 shadow 3
七、AWK中數組的使用
- 手動定義數組
# echo '' | awk '{aa[1]=1;aa[2]=2;aa["b"]="b";print aa[1]}'
1
- 讀取文件
# awk -F: '{for(i=1;i<=NF;i++){a[i]=$i;print a[1];break}}' passwd
root
bin
daemon
# cat c1
01 北京
02 上海
03 廣州
04 深圳
# cat c2
01 beijing
02 shanghai
03 guangzhou
04 shenzhen
# awk 'NR==FNR{a[$1]=$0}NR!=FNR{print a[$1],$2}' c1 c2
01 北京 beijing
02 上海 shanghai
03 廣州 guangzhou
04 深圳 shenzhen
八、AWK中常見函數的使用
sub
和gsub
函數:用來替換
# cat test.txt
tom bob mary tom
bob tom mary tom
# awk '{sub("tom","TOM");print}' test.txt
TOM bob mary tom
bob TOM mary tom
但是隻替換了每行的第一個,要想替換所有的,使用gsub
函數:
# awk '{gsub("tom","TOM");print}' test.txt
TOM bob mary TOM
bob TOM mary TOM
只替換最後一個:
# awk '{gsub("tom","TOM",$NF);print}' test.txt
tom bob mary TOM
bob tom mary TOM
length
函數用來計算字符串長度
# awk -F: '{print $1,length($1)}' passwd
root 4
bin 3
daemon 6
substr
函數用來截取一段數據
# awk -F: '{print substr($1,2)}' passwd
oot
in
aemon
# awk -F: '{print substr($1,2,2)}' passwd
oo
in
ae
match
函數的使用,從字符串中匹配滿足某種格式的位置
# awk -F: '{xx=match($1,"oo");print xx}' passwd
2
0
0
match
中有兩個變量,RSTART
可以獲取到match
得到的值,RLENGTH
表示的是match
中滿足格式的字符創的長度,一般會結合substr截取我們所需要的數據:
# awk -F: '{xx=match($1,"oo");print xx,RSTART,RLENGTH}' passwd
2 2 2
0 0 -1
0 0 -1
split
函數的使用:split("原始字符串",數組名,<分隔符>)
# echo '' | awk '{split("2018-05-20",xx,"-");print xx[1],xx[2],xx[3]}'
2018 05 20
# awk '{split($0,xx,":");print xx[1]}' passwd
root
bin
daemon
getline
函數可以讓我們使用操作系統命令,也可以讀取另一個文件內容
# awk '{getline;print $0}' c1
02 上海
04 深圳
可以看到getline
將本文件的下一行賦值給當前的$0
# awk '{getline xx;print $0,xx}' c1
01 北京 02 上海
03 廣州 04 深圳
在這裏將下一行的值賦值給了getline後的變量xx,並沒有影響當前的$0
getline < file
:該用法讀取file文件的一行賦值給當前的$0
getline xx < file
:該用法讀取file文件的一行賦值給變量xx
# awk '{getline xx< "c2";print xx,$2}' c1
01 beijing 北京
02 shanghai 上海
03 guangzhou 廣州
04 shenzhen 深圳
# echo '' | awk '{"date"|getline xx;print xx}'
2018年 05月 20日 星期日 19:57:04 CST
# echo '' | awk '{while("ls /boot"|getline xx) print xx}'
config-3.10.0-327.el7.x86_64
grub2
initramfs-0-rescue-ba7506401e184ceea617517542f6b7c7.img
initramfs-3.10.0-327.el7.x86_64.img
initrd-plymouth.img
symvers-3.10.0-327.el7.x86_64.gz
System.map-3.10.0-327.el7.x86_64
vmlinuz-0-rescue-ba7506401e184ceea617517542f6b7c7
vmlinuz-3.10.0-327.el7.x86_64
# echo '' | awk '{print "請輸入:";getline name<"/dev/tty";print "你好:" name}'
請輸入:
tom
你好:tom
九、AWK的格式化輸出
printf
不帶換行符
# awk -F: '{printf "%-8s|%-5d\n",$1,$3}' passwd
root |0
bin |1
daemon |2
其中%-8s
中的-8表示顯示寬度爲8個字符,-
表示左對齊,+
號表示右對齊%d
顯示的整數%f
顯示的是浮點數,%10.5f
表示總共顯示十位。小數點後顯示5位%e
用科學計數法顯示
# echo '' | awk '{printf "%d\n",3*3.5}'
10
# echo '' | awk '{printf "%f\n",3*3.5}'
10.500000
# echo '' | awk '{printf "%-.10f\n",10/3}'
3.3333333333
# echo '' | awk '{printf "%e\n",3*3.5}'
1.050000e+01