awk介紹
- awk:Aho, Weinberger, Kernighan,報告生成器,格式化文本輸出
- 有多種版本:New awk(nawk),GNU awk( gawk),一般我們用的就是gawk
- gawk:模式掃描和處理語言
基本語法
awk [options] ‘program’ var=value file…
awk [options] -f programfile var=value file…
awk [options] 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' file ...
awk 程序通常由:BEGIN語句塊、能夠使用模式匹配的通用語句塊、END語句塊,共3部分組成
program必須被單引號括住
工作原理
類似sed,也會向內存申請一塊空間,每次讀取文件一行,按照分隔符切割成字段——列,對於這些列,awk有專門的名稱表示: $0——整行; $1 ——第一列;$2 $3 ;然後根據需要打印特定的列(默認有一個自動打印所有列的功能)
第一步:執行BEGIN{action;… }語句塊中的語句
第二步:從文件或標準輸入(stdin)讀取一行,然後執行pattern{ action;… }語句塊,它逐行掃描文件,從第一行到最後一行重複這個過程,直到文件全部被讀取完畢。
第三步:當讀至輸入流末尾時,執行END{action;…}語句塊
BEGIN語句塊在awk開始從輸入流中讀取行之前被執行,這是一個可選的語句塊,比如變量初始化、打印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中
END語句塊在awk從輸入流中讀取完所有的行之後即被執行,比如打印所有行的分析結果這類信息彙總都是在END語句塊中完成,它也是一個可選語句塊
pattern語句塊中的通用命令是最重要的部分,也是可選的。如果沒有提供pattern語句塊,則默認執行{ print },即打印每一個讀取到的行,awk讀取的每一行都會執行該語句塊
選項
默認分隔符爲空格;
- -f:將程序放入文本中,-f調用
- -F:指定分隔符
- -v var=value:自定義變量
分隔符、域和記錄
• awk執行時,由分隔符分隔的字段(域)標記$1,$2..$n稱爲域標識。$0爲所有域,
注意:和shell中變量$符含義不同
• 文件的每一行稱爲記錄
• 省略action,則默認執行 print $0 的操作
基本格式
awk [options] 'program' file
program由兩部分組成:模式+動作
pattern:表示模式,根據pattern條件,過濾匹配的行進行處理,不寫則匹配每一行;
action:動作,常用於打印
BEGIN——表示在處理文件之前執行的語句;
END———表示在處理完文件之後執行的語句,如彙總
print格式: print item1, item2, ...
要點:
(1) 逗號分隔符
(2) 輸出的各item可以字符串,也可以是數值;當前記錄的字段、變量或awk的表達式
(3) 如省略item,相當於print $0
示例:
> 取分區的第一列和第三列
[root@centos7 ~ ]#df|awk '{print $1,$3}'
Filesystem Used
/dev/sda2 4275140
devtmpfs 0
tmpfs 0
tmpfs 10312
tmpfs 0
/dev/sda3 33112
/dev/sda1 157932
tmpfs 32
/dev/sr0 9176232
> 打印固定字符符,默認awk接收標準輸入
[root@centos7 ~ ]#awk '{print "hello,awk"}'
xin
hello,awk
haha
hello,awk
wang
hello,awk
lhy
hello,awk
^C
[root@centos7 ~ ]#
> 多個字段用tab分割,分割符需要用雙引號引起來,否則報錯
[root@centos7 ~ ]#awk -F: '{print $1"\t"$2}' /etc/passwd
root x
bin x
daemon x
adm x
lp x
sync x
shutdown x
halt x
> 取分區利用率:
[root@centos7 ~ ]#df|grep "^/dev/sd"|awk '{print $1"\t"$5}'|awk -F% '{print $1}'
/dev/sda2 9
/dev/sda3 1
/dev/sda1 16
後面還有更加簡單的方法,不信,那就接着往下看
awk變量
變量賦值必須加-v
awk變量分爲內置變量和自定義變量
內置變量
FS:輸入字段分割符,即讀取的文件中列與列之間的分隔符,爲未處理之前,默認爲空格;
[root@centos7 ~ ]#awk -v FS=: '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
> 好處:可以在{}中引用
[root@centos7 ~ ]#awk -v FS=: '{print $1FS$3}' /etc/passwd
root:0
bin:1
daemon:2
adm:3
> 並且支持shell變量
[root@centos7 ~ ]#set=:
[root@centos7 ~ ]#echo $set
:
[root@centos7 ~ ]#awk -v FS=$set '{print $1FS$3}' /etc/passwd
root:0
bin:1
daemon:2
adm:3
lp:4
OFS:輸出字段分隔符,爲處理之後的分隔符,默認爲空格;
> 指定輸出字段分割符爲----,建議對分隔符的值用引號引起來
[root@centos7 ~ ]#awk -v FS=":" -v OFS="----" '{print $1,$3}' /etc/passwd
root----0
bin----1
daemon----2
adm----3
lp----4
RS:輸入記錄分隔符,即行與行之間的分隔符,默認爲換行符;
> 準備環境:“;”行的分隔符,","爲列的分隔符;
[root@centos7 data ]#cat f1
a,b,c;1,2,3,4;A,B,C
aa,bb,cc,dd
> 打印每一行的第3列和第4列
[root@centos7 data ]#awk -v FS="," -v RS=";" '{print $3,$4}' f1
c
3 4
C
aa bb
雖然C和aa換行了,但是以逗號爲分隔符,所以輸出的字段Caa爲一列,不過保留換行;
ORS:輸出記錄分隔符,默認爲換行符
[root@centos7 data ]#cat f1
a,b,c;1,2,3,4;A,B,C
aa,bb,cc,dd
> 指定輸出的列的分隔符爲--,輸出的行的分隔符爲+++
[root@centos7 data ]#awk -v FS="," -v OFS="--" -v RS=";" -v ORS="+++" '{print $3,$4}' f1
c--+++3--4+++C
aa--bb+++[root@centos7 data ]#
可以看到Caa是在一行的,並且是一列;
NF:字段數量
> 打印/etc/passwd中每一行的字段數量
[root@centos7 data ]#awk -F: '{print NF}' /etc/passwd
7
7
7
7
7
> 打印最後一個字段
[root@centos7 data ]#awk -F: '{print $7}' /etc/passwd
/bin/bash
/sbin/nologin
/sbin/nologin>
/sbin/nologin
> 通過NF打印最後一個字段
[root@centos7 data ]#awk -F: '{print $NF}' /etc/passwd
/bin/bash
/sbin/
> 打印倒數第三個字段
[root@centos7 data ]#awk -F: '{print $NF-2}' /etc/passwd
-2
-2
-2
-2
> 注意:要加(),否則會出現上面的情況
[root@centos7 data ]#awk -F: '{print $(NF-2)}' /etc/passwd
root
bin
daemon
adm
NR: 記錄號,第幾條記錄,說白了就是行號
[root@centos7 data ]#awk -F: '{print NR,$1}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
6 sync
注意:NR後面跟多個文件時,會合並統一計數
FNR:各文件分別計數,記錄號
FILENAME:當前文件名
[root@centos7 data ]#awk -F: '{print FNR,FILENAME,$1}' /etc/passwd /etc/issue
1 /etc/passwd root
2 /etc/passwd bin
3 /etc/passwd daemon
4 /etc/passwd adm
5 /etc/passwd lp
6 /etc/passwd sync
7 /etc/passwd shutdown
8 /etc/passwd halt
9 /etc/passwd mail
ARGC:命令行參數的個數
[root@centos7 data ]#awk -F: '{print ARGC}' /etc/passwd /etc/issue
3
3
3
3
3
> 怎麼樣,有沒有很奇怪,不應該是兩個參數嗎? 看下面
ARGV:數組,保存命令行各個參數
自定義變量
- -v var=value
- 在program中直接定義
示例:
[root@centos7 data ]#awk -F: -v USER="username" -v UID="userid" -v ORS="\n-------\n" '{print USER":"$1"\n"UID":"$3}' /etc/passwd
username:root
userid:0
-------
username:bin
userid:1
-------
username:daemon
userid:2
-------
[root@centos7 data ]#awk -F: '{USER="username";UID="userid";print USER":"$1"\n"UID":"$3}' /etc/passwd
username:root
userid:0
username:bin
userid:1
username:daemon
userid:2
username:adm
變量必須先定義,再使用;
-f的用法————調用文件中的program
[root@centos7 data ]#cat awk.txt
{USER="username";UID="userid";print USER":"$1"\n"UID":"$3}
[root@centos7 data ]#awk -F: -f awk.txt /etc/passwd
username:root
userid:0
username:bin
userid:1
printf命令
格式化輸出;
語法:
printf “FORMAT”, item1, item2,
- 必須指定FORMAT
- 不會自動換行,需要顯式給出換行控制符,\n
-
FORMAT中需要分別爲後面每個item指定格式符
-
格式化符,常用的是%s和%d
格式符:與item一一對應 %c: 顯示字符的ASCII碼 %d, %i: 顯示十進制整數 %e, %E:顯示科學計數法數值 %f:顯示爲浮點數 %g, %G:以科學計數法或浮點形式顯示數值 %s:顯示字符串 %u:無符號整數 %%: 顯示%自身
-
修飾符,
#[.#]:第一個數字控制顯示的寬度;第二個#表示小數點後精度,%3.1f -: 左對齊(默認右對齊) %-15s +:顯示數值的正負符號 %+d
示例:
awk -F: ‘{printf "%s",$1}’ /etc/passwd
awk -F: ‘{printf "%s\n",$1}’ /etc/passwd
awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F: ‘{printf "Username: %s\n",$1}’ /etc/passwd
awk -F: ‘{printf “Username: %s,UID:%d\n",$1,$3}’ /etc/passwd
awk -F: ‘{printf "Username: %15s,UID:%d\n",$1,$3}’ /etc/passwd
awk -F: ‘{printf "Username: %-15s,UID:%d\n",$1,$3}’ /etc/passwd
-F指定多分隔符
面試題
實驗環境:
[root@centos7 data ]#cat host.txt
1 blog.xin.com
2 www.wang.com
3 good.xin.com
999 study.wang.com
實驗要求:取出每一行中的主機名,如blog、www等,然後追加到該文本中。
步驟:
[root@centos7 data ]#awk -F"[ .]" '{print $2}' host.txt >> host.txt
[root@centos7 data ]#cat host.txt
1 blog.xin.com
2 www.wang.com
3 good.xin.com
999 study.wang.com
blog
www
good
study
操作符
算術操作符
x+y, x-y, x*y, x/y, x^y, x%y
-x: 轉換爲負數
+x: 轉換爲數值
字符串操作符
沒有符號的操作符,字符串連接
賦值操作符
=, +=, -=, *=, /=, %=, ^=
++, --
注意點:i++和++i的區別
++i和i++雖然都會對i進行+1賦值給i,但是順序是不一樣的:
i++是先使用i,再去進行+運算;
++i是先運算,再去使用i;
比較操作符
==, !=, >, >=, <, <=
示例:
[root@centos7 data ]#awk -F: '$3==0{print $1}' /etc/passwd
root
[root@centos7 data ]#awk -F: '$3!=0{print $1}' /etc/passwd
bin
daemon
adm
......
[root@centos7 data ]#awk -F: '$3>=1000{print $1}' /etc/passwd
nfsnobody
xin
lhy
模式匹配符
~:左邊是否包含右邊的字符
!~:不包含
示例:
> $0即整行,是否包含root字符的行
[root@centos7 data ]#awk -F: '$0 ~ /root/{print $1}' /etc/passwd
root
operator
> 雖然使用雙引號也可以,但是不建議,有些時候會報錯,並且//之間支持正則表達式
[root@centos7 data ]#awk -F: '$0 ~ "root"{print $1}' /etc/passwd
root
operator
> $0中以root開頭的行
[root@centos7 data ]#awk -F: '$0 ~ /^root/{print $1}' /etc/passwd
root
> 不以root開頭的行,取反
[root@centos7 data ]#awk -F: '$0 !~ /^root/{print $1}' /etc/passwd
bin
daemon
adm
......
注意:模式匹配符可以不用,因爲awk默認直接可以匹配正則,詳情往先看
邏輯操作符
邏輯操作符:與&&,或||,非!
示例:
• awk –F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd
• awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd
• awk -F: ‘!($3==0) {print $1}' /etc/passwd
• awk -F: ‘!($3>=500) {print $3}’ /etc/passwd
注意:取反要加小括號,否則報錯
條件表達式(三目表達式):
selector?if-true-expression:if-false-expression
即:A? B:C
A爲條件,B和C是動作;如果A爲真,則執行B;否則執行C;
注意B和C之間的分隔符爲:
示例:
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf
"%15s:%-s\n",$1,usertype}' /etc/passwd
awk PATTERN
直接支持正則,不需要$0 ~等額外的字符
-
如果未指定:空模式,匹配每一行
- /regular expression/:僅處理能夠模式匹配到的行,需要用/ /括起來
如:awk '/^UUID/{print $1}' /etc/fstab
- print省略後,打印整行
> 過濾分區,前面使用的是grep,現在完全可以都使用awk解決了
[root@centos7 data ]#df |awk '/^\/dev\/sd/'
/dev/sda2 52403200 4276856 48126344 9% /
/dev/sda3 31441920 33120 31408800 1% /data
/dev/sda1 1038336 157932 880404 16% /boot
> 統計/etc/fstab文件中文件系統的類型並統計次數
[root@centos7 data ]#awk '/^UUID/{print $3}' /etc/fstab|sort|uniq -c
1 swap
3 xfs
> 統計netstat -nt中外部ip地址連接的次數,並按照連接次數進行排序
[root@centos7 data ]#netstat -nt|awk -F'[ :]+' '/^tcp/{print $6}'|sort|uniq -c|sort -nr
1 192.168.137.8
1 192.168.137.6
1 192.168.137.1
面試題:統計netstat -nt中併發連接數排在前10名的ip和連接數
- relational expression: 關係表達式,結果爲“真”纔會被處理
真:結果爲非0值,非空字符串
假:結果爲空字符串或0值
> 由於""爲空,則爲假,不打印
[root@centos7 data ]#awk -F: '""{print $1}' /etc/passwd
> 由於空格爲真,則打印
[root@centos7 data ]#awk -F: '" "{print $1}' /etc/passwd
root
bin
daemon
> 爲0則爲假不打印;爲1則爲真打印
[root@centos7 data ]#awk -F: '0{print $1}' /etc/passwd
[root@centos7 data ]#awk -F: '1{print $1}' /etc/passwd
root
bin
daemon
adm
> true打印,false不打印
[root@centos7 data ]#seq 10|awk "i=9"
1
2
3
4
5
6
7
8
9
10
[root@centos7 data ]#seq 10|awk "i=0"
[root@centos7 data ]#
> 打印奇數:
[root@centos7 data ]#seq 10|awk 'i=!i'
1
3
5
7
9
說明:由於i最開始賦值爲空,則第一次!i=1,打印1;第二次!i=0,不打印2;第三次!i=1.打印i=3;第四次不打印,第五次打印5;所以打印的是奇數:即 1 3 5 7 9,可以通過seq 10|awk '{i=!i;print i}'查看執行過程
> 打印偶數
方法1:取反
[root@centos7 data ]#seq 10|awk '!(i=!i)'
2
4
6
8
10
方法2:直接對i賦值,任意非空值即可
[root@centos7 data ]#seq 10|awk -v i=1 'i=!i'
2
4
6
8
10
類比:sed打印奇數和偶數
[root@centos7 data ]#seq 10|sed -n '1~2p'
1
3
5
7
9
[root@centos7 data ]#seq 10|sed -n '2~2p'
2
4
6
8
10
-
行範圍
類比sed取行的方式: [root@centos7 data ]#sed -n '1,3p' /etc/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 > 模式匹配 [root@centos7 data ]#sed -n '/^b/,/^f/p' /etc/passwd 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 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin awk數字匹配行 [root@centos7 data ]#awk -F: '(NR>=1&&NR<=3){print NR,$0}' /etc/passwd 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin awk模式匹配行 [root@centos7 data ]#awk -F: '/^root\>/,/^nobody\>/{print $1}' /etc/passwd root bin daemon adm lp sync shutdown halt mail operator games ftp nobody
- BEGIN/END模式
BEGIN{} :處理文件之前執行
END{} : 處理文件之後執行
示例:
[root@centos7 data ]#awk -F: 'BEGIN {print "USER:XIN"}{print $0}END{print "end file"}' /etc/issue
USER:XIN
\S
Kernel \r on an \m
end file