1继蜡、awk
1.1 認(rèn)識(shí)awk
awk是一種編程語言日裙,用于在linux/unix下對(duì)文本和數(shù)據(jù)進(jìn)行處理溺健。數(shù)據(jù)可以來自標(biāo)準(zhǔn)輸入(stdin)麦牺、一個(gè)或多個(gè)文件,或其它命令的輸出鞭缭。它支持用戶自定義函數(shù)和動(dòng)態(tài)正則表達(dá)式等先進(jìn)功能剖膳,是linux/unix下的一個(gè)強(qiáng)大編程工具。它在命令行中使用岭辣,但更多是作為腳本來使用吱晒。awk有很多內(nèi)建的功能,比如數(shù)組沦童、函數(shù)等仑濒,這是它和C語言的相同之處,靈活性是awk最大的優(yōu)勢搞动。
awk其實(shí)不僅僅是工具軟件,還是一種編程語言渣刷。不過鹦肿,本文只介紹它的命令行用法,對(duì)于大多數(shù)場合辅柴,應(yīng)該足夠用了箩溃。
1.2 使用awk
1.2.1 語法
awk
[options]
'program'
var=value
file``…
awk
[options] -f programfile var=value
file``…
awk
[options]
'BEGIN{ action;… } pattern{ action;… } END{ action;… }'
file
...
1.2.2 常用命令選項(xiàng)
- -F fs:fs指定輸入分隔符瞭吃,fs可以是字符串或正則表達(dá)式,如-F:
- -v var=value:賦值一個(gè)用戶定義變量涣旨,將外部變量傳遞給awk
- -f scripfile:從腳本文件中讀取awk命令
1.3 awk變量
變量:內(nèi)置和自定義變量歪架,每個(gè)變量前加 -v 命令選項(xiàng)
1.3.1 內(nèi)置變量
(1)格式
- FS :輸入字段分隔符,默認(rèn)為空白字符
- OFS :輸出字段分隔符霹陡,默認(rèn)為空白字符
- RS :輸入記錄分隔符和蚪,指定輸入時(shí)的換行符,原換行符仍有效
- ORS :輸出記錄分隔符烹棉,輸出時(shí)用指定符號(hào)代替換行符
-
NF :字段數(shù)量攒霹,共有多少字段,
(NF-1)引用倒數(shù)第2列
- NR :行號(hào)催束,后可跟多個(gè)文件,第二個(gè)文件行號(hào)繼續(xù)從第一個(gè)文件最后行號(hào)開始
- FNR :各文件分別計(jì)數(shù), 行號(hào)伏社,后跟一個(gè)文件和NR一樣抠刺,跟多個(gè)文件,第二個(gè)文件行號(hào)從1開始
- FILENAME :當(dāng)前文件名
- ARGC :命令行參數(shù)的個(gè)數(shù)
- ARGV :數(shù)組摘昌,保存的是命令行所給定的各參數(shù)速妖,查看參數(shù)
$ cat awkdemo
hello:world
linux:redhat:lalala:hahaha
kevin:love:youou
- -FS :輸入字段分隔符,默認(rèn)為空白字符
$ awk -v FS=":" '{print $1,$2}' awkdemo
hello world
linux redhat
kevin love
當(dāng)然也可以寫成另外一種形式:
$ awk -F: '{print $1,$2}' awkdemo
hello world
linux redhat
kevin love
另外通過awk命令可以指定自己所需要的字符第焰,比如e:
awk -v FS="e:" '{print $1,$2}' awkdemo
hello:world
linux:redhat:lalala:hahaha
kevin:lov youou
但是如果用cut
命令的話會(huì)導(dǎo)致報(bào)錯(cuò)买优,因?yàn)橥ㄟ^cut
指定的分割符只能是單個(gè)字符,但是awk
可以指定多個(gè)字符作為分割符
$ cut -d "e:" -f1,2 awkdemo
cut: the delimiter must be a single character
Try 'cut --help' for more information.
- -OFS指定輸出分隔符
awk -v FS=':' -v OFS='---' '{print $1,$2}' awkdemo
這樣便通過awk的方法將文本中的:
分隔符變成---
hello---world
linux---redhat
kevin---love
- NF :字段數(shù)量挺举,共有多少字段杀赢, $NF引用最后一列,$(NF-1)引用倒數(shù)第2列
$ awk -F: {'print NF'} awkdemo
2
4
3
- NR :行號(hào)湘纵,后可跟多個(gè)文件脂崔,第二個(gè)文件行號(hào)繼續(xù)從第一個(gè)文件最后行號(hào)開始
$ awk '{print NR}' awkdemo awkdemo
1
2
3
4
5
6
- FNR :各文件分別計(jì)數(shù), 行號(hào),后跟一個(gè)文件和NR一樣梧喷,跟多個(gè)文件砌左,第二個(gè)文件行號(hào)從1開始
$ awk '{print FNR}' awkdemo awkdemo
1
2
3
1
2
3
- 可以直接通過awk來統(tǒng)計(jì)這兩個(gè)文件一共多少行
$ awk END'{print NR}' awkdemo awkdemo
6
- FILENAME :當(dāng)前文件名
$ awk "{print FILENAME}" awkdemo demo
awkdemo
awkdemo
awkdemo
demo
demo
demo
demo
1.3.2 自定義變量
自定義變量( 區(qū)分字符大小寫)
(1)-v var=value
① 先定義變量,后執(zhí)行動(dòng)作print
相當(dāng)于在我的當(dāng)前的文件的前面可以添加任何我想要的字段
$ awk -v name="kevin" -F: '{print name":"$0}' awkdemo
kevin:hello:world
kevin:linux:redhat:lalala:hahaha
kevin:kevin:love:youou
② 在執(zhí)行動(dòng)作print后定義變量
$ awk -F: '{print name":"$0, name="kevin"}' awkdemo
:hello:world kevin
kevin:linux:redhat:lalala:hahaha kevin
kevin:kevin:love:youou kevin
(2)在program 中直接定義
可以把執(zhí)行的動(dòng)作放在腳本中铺敌,直接調(diào)用腳本 -f
$ awk -F: -f awk.txt awkdemo
kevin hello
kevin linux
kevin kevin
1.4 printf命令
比print更強(qiáng)大
1.4.1 格式
(1)格式化輸出
printf
"FORMAT"``, item1,item2, ...
① 必須指定FORMAT
② 不會(huì)自動(dòng)換行汇歹,需要顯式給出換行控制符,\n
③ FORMAT 中需要分別為后面每個(gè)item 指定格式符
(2)格式符:與item 一一對(duì)應(yīng)
- %c: 顯示字符的ASCII碼
- %d, %i: 顯示十進(jìn)制整數(shù)
- %e, %E: 顯示科學(xué)計(jì)數(shù)法數(shù)值
- %f :顯示為浮點(diǎn)數(shù)偿凭,小數(shù) %5.1f产弹,帶整數(shù)、小數(shù)點(diǎn)弯囊、整數(shù)共5位痰哨,小數(shù)1位胶果,不夠用空格補(bǔ)上
- %g, %G :以科學(xué)計(jì)數(shù)法或浮點(diǎn)形式顯示數(shù)值
- %s :顯示字符串;例:%5s最少5個(gè)字符斤斧,不夠用空格補(bǔ)上早抠,超過5個(gè)還繼續(xù)顯示
- %u :無符號(hào)整數(shù)
- %%: 顯示% 自身
(3)修飾符:放在%c[/d/e/f...]之間
-
#[.#]:
第一個(gè)數(shù)字控制顯示的寬度;第二個(gè)# 表示小數(shù)點(diǎn)后精度撬讽,%5.1f - -:左對(duì)齊(默認(rèn)右對(duì)齊) %-15s
- +:顯示數(shù)值的正負(fù)符號(hào) %+d
1.4.2 演示
當(dāng)使用print
的時(shí)候:
$ awk -F: '{print $1,$3}' /etc/passwd | head -n 2
root 0
bin 1
- 第一列顯示小于20的字符串蕊连;第2列顯示整數(shù)并換行
$ awk -F: '{printf "%20s---%u\n",$1,$3}' /etc/passwd | head -n 2
root---0
bin---1
- 使用-進(jìn)行左對(duì)齊;第2列顯示浮點(diǎn)數(shù)
$ awk -F: '{printf "%-20s---%-15.3f\n",$1,$3}' /etc/passwd | head -n 2
root ---0.000
bin ---1.000
1.5 操作符
1.5.1 格式
- 算術(shù)操作符:
- x+y, x-y, x*y, x/y, x^y, x%y
- -x: 轉(zhuǎn)換為負(fù)數(shù)
- +x: 轉(zhuǎn)換為數(shù)值
- 字符串操作符:沒有符號(hào)的操作符锐秦,字符串連接
- 賦值操作符:
- =, +=, -=, *=, /=, %=, ^=
- ++, --
- 比較操作符:
- ==, !=, >, >=, <, <=
- 模式匹配符:~ :左邊是否和右邊匹配包含 !~ :是否不匹配
- 邏輯操作符:與&& 咪奖,或|| ,非!
- 函數(shù)調(diào)用: function_name(argu1, argu2, ...)
- 條件表達(dá)式(三目表達(dá)式):selector?if-true-expression:if-false-expression
- 注釋:先判斷selector酱床,如果符合執(zhí)行 ? 后的操作羊赵;否則執(zhí)行 : 后的操作
1.5.2 演示
(1)模式匹配符
- 模式匹配符:~ :左邊是否和右邊匹配包含 !~ :是否不匹配
$ df -h |awk -F: '$0 ~ /^\/dev/'
/dev/mapper/cl-root 165G 81G 84G 50% /
/dev/sdb1 37T 30T 7.0T 81% /GP
/dev/sda1 197M 155M 43M 79% /boot
/dev/mapper/cl-var 50G 1.8G 49G 4% /var
/dev/sdc1 3.7T 2.9T 788G 79% /media/sdisk/usb2
/dev/sdd1 1.9T 1.2T 728G 61% /media/sdisk/usb3
這種操作模式有些類似于
$ df -h |awk -F: '$0' | grep '/dev/'
tmpfs 126G 21G 106G 17% /dev/shm
/dev/mapper/cl-root 165G 81G 84G 50% /
/dev/sdb1 37T 30T 7.0T 81% /GP
/dev/sda1 197M 155M 43M 79% /boot
/dev/mapper/cl-var 50G 1.8G 49G 4% /var
/dev/sdc1 3.7T 2.9T 788G 79% /media/sdisk/usb2
/dev/sdd1 1.9T 1.2T 728G 61% /media/sdisk/usb3
結(jié)合之前提到的NF :字段數(shù)量,共有多少字段扇谣, (NF-1)引用倒數(shù)第2列
只顯示磁盤使用狀況和磁盤名
$ df -h | awk '$0 ~ /^\/dev/ {print $(NF-1)"---"$1}'
50%---/dev/mapper/cl-root
81%---/dev/sdb1
79%---/dev/sda1
4%---/dev/mapper/cl-var
79%---/dev/sdc1
61%---/dev/sdd1
找出磁盤占比大于40%的
相當(dāng)于在之前的命令的基礎(chǔ)上添加了應(yīng)該對(duì)第一行的篩選
$ df -h | awk '$0 ~ /^\/dev/ {print $(NF-1)"---"$1}' |awk -F% '$1>40'
50%---/dev/mapper/cl-root
81%---/dev/sdb1
79%---/dev/sda1
79%---/dev/sdc1
61%---/dev/sdd1
(2)邏輯操作符
$ awk -F: '$3 >=6 && $3<=66 {print $1,$3}' /etc/passwd
shutdown 6
halt 7
mail 8
uucp 10
operator 11
games 12
gopher 13
ftp 14
rpc 32
oprofile 16
rpcuser 29
gdm 42
ntp 38
apache 48
mailnull 47
smmsp 51
mysql 27
pegasus 66
postgres 26
$ awk -F: '$3==0 || $3>1000 {print $1,$3}' /etc/passwd
root 0
nfsnobody 65534
(3)條件表達(dá)式(三目表達(dá)式)
- 先判斷selector,如果符合執(zhí)行 ? 后的操作罐寨;否則執(zhí)行 : 后的操作
$ awk -F: '{$3 >= 1000?usertype="common user":usertype="sysadmin user";print usertype,$1,$3}' /etc/passwd | head -n 30
sysadmin user root 0
sysadmin user bin 1
sysadmin user daemon 2
sysadmin user adm 3
sysadmin user lp 4
sysadmin user sync 5
sysadmin user shutdown 6
sysadmin user halt 7
sysadmin user mail 8
sysadmin user uucp 10
sysadmin user operator 11
sysadmin user games 12
sysadmin user gopher 13
sysadmin user ftp 14
sysadmin user nobody 99
sysadmin user dbus 81
sysadmin user usbmuxd 113
sysadmin user rpc 32
sysadmin user oprofile 16
sysadmin user vcsa 69
sysadmin user rtkit 499
sysadmin user abrt 173
sysadmin user hsqldb 96
sysadmin user avahi-autoipd 170
sysadmin user saslauth 498
sysadmin user postfix 89
sysadmin user rpcuser 29
common user nfsnobody 65534
sysadmin user haldaemon 68
sysadmin user gdm 42
1.6 awk PATTERN 匹配部分
1.6.1 格式
PATTERN:根據(jù)pattern 條件靡挥,過濾匹配的行,再做處理
(1)如果未指定:空模式鸯绿,匹配每一行
(2)/regular expression/ :僅處理能夠模式匹配到的行跋破,正則,需要用/ / 括起來
(3)relational expression:關(guān)系表達(dá)式瓶蝴,結(jié)果為“真”才會(huì)被處理
真:結(jié)果為非0值毒返,非空字符串
假:結(jié)果為空字符串或0值
(4)line ranges:行范圍
startline(起始行),endline(結(jié)束行):/pat1/,/pat2/ 不支持直接給出數(shù)字,可以有多段舷手,中間可以有間隔
(5)BEGIN/END 模式
BEGIN{}: 僅在開始處理文件中的文本之前執(zhí)行一次
END{} :僅在文本處理完成之后執(zhí)行
1.6.2 演示
$ awk -F: '{print $1,$2}' awkdemo
hello world
linux redhat
kevin love
- /regular expression/ :僅處理能夠模式匹配到的行拧簸,正則,需要用/ / 括起來
$ awk -F: '/kevin/{print $1,$2}' awkdemo
kevin love
relational expression:關(guān)系表達(dá)式男窟,結(jié)果為“真”才會(huì)被處理 ; 真:結(jié)果為非0值盆赤,非空字符串
$ awk -F: "1{print $1}" awkdemo
hello:world
linux:redhat:lalala:hahaha
kevin:love:youou
結(jié)果為空字符串或0值時(shí),以下代碼則不會(huì)被執(zhí)行
$ awk -F: "0{print $1}" awkdemo
- 匹配第一個(gè)首字母為
h
到第一個(gè)首字母為a
的之間所有字符
$ awk -F: '/^h/,/^a/ {print $1}' awkdemo
hello
linux
kevin
- BEGIN/END 模式
- BEGIN{}: 僅在開始處理文件中的文本之前執(zhí)行一次
- END{} :僅在文本處理完成之后執(zhí)行
$ awk -F: 'BEGIN{print "第一列"}{print $1} END{print "結(jié)束"}' awkdemo
第一列
hello
linux
kevin
結(jié)束
1.7 awk有意思的案例
$ seq 10
1
2
3
4
5
6
7
8
9
10
因?yàn)閕=1為真歉眷,所以全部打印
$ seq 10 | awk 'i=1'
1
2
3
4
5
6
7
8
9
10
因?yàn)閕=0為假牺六,所以不打印
$ seq 10 | awk 'i=0'
只打印奇數(shù)行;奇數(shù)行i進(jìn)入時(shí)本身為空汗捡,被賦為!i淑际,即不為空,所以打印庸追;偶數(shù)行i進(jìn)入時(shí)本身不為空,被賦為!i台囱,即為空淡溯,所以不打印
$ seq 10 | awk 'i=!i'
1
3
5
7
9
解釋上一個(gè)操作,i在奇偶行的時(shí)候到底是一個(gè)什么樣的值
$ seq 10 |awk '{i=!i;print i}'
1
0
1
0
1
0
1
0
1
0
當(dāng)然你也可以選擇只打印偶數(shù)行簿训,相當(dāng)于是上邊打印奇數(shù)行的取反
$ seq 10 | awk '!(i=!i)'
2
4
6
8
10
當(dāng)然為了打印出偶數(shù)行咱娶,我們也可以對(duì)i
進(jìn)行賦值,這樣i
在最開始的時(shí)候就不為空强品,剛好與打印奇數(shù)行的時(shí)候相反
$ seq 10 | awk -v 'i=1' 'i=!i'
2
4
6
8
10
1膘侮、awk高階用法
1.1 awk控制語句—if-else判斷
(1)語法
if
(condition){statement;…}
[else statement] 雙分支
if
(condition1){statement1}
else
if
(condition2){statement2}
else
{statement3} 多分支
(2)使用場景:對(duì)awk 取得的整行或某個(gè)字段做條件判斷
(3)演示
$ awk -F: '{if($3>10 && $3<20)print $1,$3}' /etc/passwd
operator 11
games 12
gopher 13
ftp 14
oprofile 16
打印出所有路徑為/bin/bash
的所有用戶名以及路徑
$ awk -F: '{if($NF=="/bin/bash") print $1,$NF}' /etc/passwd | head -n 10
root /bin/bash
chenys /bin/bash
liutao /bin/bash
git-admin /bin/bash
sysadmin /bin/bash
zhanglc /bin/bash
mysql /bin/bash
changlp /bin/bash
zhanghc /bin/bash
wangt /bin/bash
輸出總例數(shù)大于3的行
$ awk -F: '{if(NF>2) print $0}' awkdemo
linux:redhat:lalala:hahaha
kevin:love:youou
第三列大于等于100的為Common user用戶, 反之則為root or Sysuser用戶
$ awk -F: '{if($3>=100) {printf "Common user: %s\n",$1} else{printf "root or Sysuser : %s\n",$1}}'
/etc/passwd | head -n 20
root or Sysuser : root
root or Sysuser : bin
root or Sysuser : daemon
root or Sysuser : adm
root or Sysuser : lp
root or Sysuser : sync
root or Sysuser : shutdown
root or Sysuser : halt
root or Sysuser : mail
root or Sysuser : uucp
root or Sysuser : operator
root or Sysuser : games
root or Sysuser : gopher
root or Sysuser : ftp
root or Sysuser : nobody
root or Sysuser : dbus
Common user: usbmuxd
root or Sysuser : rpc
root or Sysuser : oprofile
root or Sysuser : vcsa
找到磁盤利用率超過40的設(shè)備名以及利用率
$ df -h | awk -F% '/^\/dev/ {print $1}' | awk '$NF>40{print $1,$NF}'
/dev/sda3 63
/dev/sdb2 74
/dev/sda2 69
做一個(gè)判斷的榛,當(dāng)test
>=90的時(shí)候?yàn)閑xcellent琼了,當(dāng)60 < test
<90的時(shí)候?yàn)閜ass,當(dāng)test
<60的時(shí)候?yàn)閒ailed
$ awk 'BEGIN{test=100; if(test>=90){print "excellent"} else if(test>=60 && test <90){print "pass"} else{print "failed"}}'
excellent
$ awk 'BEGIN{test=55; if(test>=90){print "excellent"} else if(test>=60 && test <90){print "pass"} else{print "failed"}}'
failed
$ awk 'BEGIN{test=65; if(test>=90){print "excellent"} else if(test>=60 && test <90){print "pass"} else{print "failed"}}'
pass
1.2 awk控制語句—while循環(huán)
(1)語法
while``(condition){statement;…}
注:條件“真”夫晌,進(jìn)入循環(huán)雕薪;條件“假”, 退出循環(huán)
(2)使用場景
對(duì)一行內(nèi)的多個(gè)字段逐一類似處理時(shí)使用
對(duì)數(shù)組中的各元素逐一處理時(shí)使用
(3)演示
$ awk -F: '/^kevin/{i=1; while(i<=NF){print $i, length($i); i++}}' awkdemo
kevin 5
love 4
youou 5
以:
為分隔晓淀,顯示每一行的長度大于6的單詞和其長度
$ awk -F: '{i=1;while(i<=NF) {if(length($i)>=6){print $i,length($i)}; i++}}'
awkdemo
redhat 6
lalala 6
hahaha 6
計(jì)算從1加到100的和
$ awk 'BEGIN{i=1;sum=0;while(i<=100){sum=sum+i;i++} {print sum}}'
5050
1.3 awk控制語句—do-while循環(huán)
(1)語法
do
{statement;…}
while
(condition)
意義:無論真假所袁,至少執(zhí)行一次循環(huán)體
(2)計(jì)算1+2+3+...+100=5050
$ awk 'BEGIN{i=1; sum=0; do{sum=sum+i;i++}while(i<=100) {print sum}}'
5050
1.4 awk控制語句—for循環(huán)
(1)語法
for``(expr1;expr2;expr3) {statement;…}
(2)特殊用法:遍歷數(shù)組中的元素
for``(var
in
array) {``for``-body}
(3)演示
$ awk -F: '{for(i=1;i<=NF;i++){print $i,length($i)}}' awkdemo
hello 5
world 5
linux 5
redhat 6
lalala 6
hahaha 6
kevin 5
love 4
youou 5
$ cat sort.txt
xiaoming m 90
xiaohong f 93
xiaohei m 80
xiaofang f 99
統(tǒng)計(jì)男m卡啰、女f 各自的平均分
$ awk '{m[$2]++; score[$2]+=$3} END{for(i in m){printf "%s:%6.2f\n",i,score[i]/m[i]}}' sort.txt
m: 85.00
f: 96.00
1.5 數(shù)值\字符串處理
(1)數(shù)值處理
- rand():返回0和1之間一個(gè)隨機(jī)數(shù)览芳,需有個(gè)種子 srand(),沒有種子谅辣,一直輸出0.237788
演示:
$ awk 'BEGIN{print rand()}'
0.237788
當(dāng)加了srand()之后懦窘,就可以正常輸出隨機(jī)數(shù)了
$ awk 'BEGIN{srand();print rand()}'
0.625523
$ awk 'BEGIN{srand();print rand()}'
0.592696
$ awk 'BEGIN{srand(); print int(rand()*100%50)+1}'
21
(2)字符串處理:
- length([s]) :返回指定字符串的長度
- sub(r,s,[t]) :對(duì)t 字符串進(jìn)行搜索r 表示的模式匹配的內(nèi)容前翎,并將第一個(gè)匹配的內(nèi)容替換為s
- gsub(r,s,[t]) :對(duì)t 字符串進(jìn)行搜索r 表示的模式匹配的內(nèi)容,并全部替換為s 所表示的內(nèi)容
- split(s,array,[r]) :以r 為分隔符奶赠,切割字符串s 鱼填,并將切割后的結(jié)果保存至array 所表示的數(shù)組中,第一個(gè)索引值為1, 第二個(gè)索引值為2,…
演示:
將前面文本中的第一個(gè):
改變成-
$ echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08
$ echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
2008-08-08 08-08-08
- split(s,array,[r]) :以r 為分隔符毅戈,切割字符串s 苹丸,并將切割后的結(jié)果保存至array 所表示的數(shù)組中,第一個(gè)索引值為1, 第二個(gè)索引值為2,…
$ echo "2008:08:08 08:08:08" | awk '{split($0,i,":")} {for(n in i){print n,i[n]}}'
1 2008
2 08
3 08 08
4 08
5 08
1.8 awk中調(diào)用shell 命令
(1)system 命令
空格是awk 中的字符串連接符苇经,如果system中需要使用awk中的變量可以使用空格分隔赘理,或者說除了awk 的變量外其他一律用"" 引用 起來。
$ awk 'BEGIN{system("hostname")}'
R290-1.GenePlus
$ awk 'BEGIN{score=100; system("echo your score is " score)}'
your score is 100