Linux常用命令-awk

命令

awk=gawk

描述

pattern scanning and processing language
文本处理工具

用法

1
2
awk [options] [--] 'program' file ...
awk [options] -f program-file [--] file ...

选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
Options:
-f program-file,--file=program-file 读取awk脚本文件
-F fs,--field-separator=fs 指定字段分隔符,默认为空白符(即空格或tab)
-v var=val,--assign=var=val 自定义变量,可用于AWK程序的BEGIN块
-d[file],--dump-variables[=file] 显示awk默认的全局变量,默认文件名awkvars.out
program: '[pattern]{action}'

内建变量Built-in Variables:
$0, $1, $2,... 字段标记$1,$2,...$n. $0表示所有字段
FS 输入字段分隔符,默认为空格,field separator
RS 输入记录分隔符,默认为换行符,record separator
OFS 输出字段分隔符,默认为空格,output field separator
ORS 输出记录分隔符,默认为换行符,output record separator
FNR 显示文件的行号,指定多个文件时分开显示行号
NF 显示列数,number of fields
NR 显示行数,number of records
OFMT 数字输出的格式,默认为%.6g
ARGC 命令行参数个数,包括awk命令本身,即显示为参数个数+1
ARGV 命令行参数数组
FILENAME 显示当前文件名
IGNORECASE 区分大小写变量,数值为0则区分大小写,为1则忽略大小写,默认 IGNORECASE=0
注意如果设置IGNORECASE=1,即忽略大小写,那么包括正则匹配,sub(),gsub()等内建函数也会忽略大小写,但数组array不受此影响

内建函数
int() 显示整数
sub(r, s [, t]) 匹配替换字符,如sub(/user/,"name"),替换匹配到的第一个字段中的user为name,注意字符串需要加引号,t表示字段,默认为$0
gsub(r, s [, t]) 全局匹配替换字符,如gsub(/user/,"name"),全局替换user为name,注意字符串需要加引号
substr(s, i [, n]) 显示指定长度的字符,从i开始到n之间的字符,如果n省略,则显示从i开始到s字段的末尾之间的字符,s表示字段$0,$1,$2...
tolower(str) 转换为小写字母
toupper(str) 转换为大写字母
length([s]) 返回字符串长度,s默认为$0
system(command) 执行linux命令,并显示执行结果,注意command需要用双引号括起来,如system("ls -al")
command | getline [var] 获取command命令的执行结果,注意command需要用双引号括起来,定义给指定变量var,默认指定给$0
systime() 显示时间秒数,从1970-01-01 00:00:00 UTC到当前时间的秒数
strftime([format [, timestamp[, utc-flag]]]) 指定时间格式,不加参数默认显示为系统时间格式,指定格式参考date命令,如strftime("%F-%T")

控制语句-条件判断和循环语句格式
if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
delete array[index]
delete array
exit [ expression ]
{ statements }
switch (expression) {
case value|regex : statement
...
[ default: statement ]
}

printf格式化输出
使用printf命令必须指定"FORMAT",支持以下字符格式用于格式转换,必须为每个item指定字符类型
格式: printf "FORMAT",item1,item2...

字符格式
\\ 反斜杠\本身
\a 警告声
\b 退格键backspace
\f 换页符form-feed
\n 换行符newline
\r 回车carriage return
\t 水平tab
\v 垂直tab
\xhex 转换字符,hex为十六进制字符,例如\x24表示$符

字符类型
%c 显示为ASCII码
%d,%i 显示十进制整数
%e,%E 显示科学计数法数值
%f 显示为浮点数
%g,%G 显示科学计数法或浮点形式数值
%s 显示字符串
%u 无符号整数
%% 显示%本身

字符长度
#[.#] 第一个#控制显示的宽度,第二个#表示小数点后的精度,如%3.2f
- 左对齐,默认右对齐,如%-15s
+ 显示数值的正负符号,如%+d
%ns n表示数字,s代表字符串string,即占多少个字符位,用于字段补齐,如%6s表示字段占用6个字符位
%ni n表示数字,i代表整数integer,即占多少个整数位
%N.nf N和n都是数字,f代表浮点数float,N表示整个浮点数位数,n表示小数位数,如%8.2f表示浮点数长度为8,其中保留2位小数

运算符按优先级递减顺序如下
(...) 分组,优先级最高
$ 引用变量
++ -- 递增和递减,包括前缀和后缀,如a++,++a
^ 求幂运算(**也可用于赋值运算符,**=用于赋值运算符)
+ - ! 一元加号,一元减号和逻辑否定
* / % 乘法,除法和取模
+ - 加减法
space 字符串连接
| |& 用于getline,print和printf的管道I/O
< > <= >= != == 正则关系运算符
~ !~ 正则表达式匹配,否定匹配.注意正则表达式在~符号的右边,如$0~/sh$/,即匹配文件中以sh结尾的行
in 数组成员匹配
&& 逻辑与
|| 逻辑或
?: C语言条件表达式, expr1 ? expr2 : expr3, 如果expr1是true,则返回值为expr2,否则返回expr3
= += -= *= /= %= ^= 赋值,支持绝对赋值(var=value)和运算符赋值

运算符分类
算术运算符:
x+y, x-y, x*y, x/y, x^y, x%y
赋值运算符:
=, +=, -=, *=, /=, %=, ^=, ++, --
比较运算符:
< > <= >= != ==
模式匹配符:
~ 左边是字符串,右边是匹配规则,如$0~/sh$/,即找出文件中匹配右侧规则的行
!~ 不匹配,取反
逻辑运算符:
与&& 或|| 非!

正则表达式参考grep命令

特殊匹配模式
BEGIN 在处理数据之前执行的语句,常用于设置变量,如BEGIN{FS=":";OFS=","}
END 在处理完数据之后
BEGINFILE 用于处理多个文件,在读取每个命令行输入文件的第一条记录之前
ENDFILE 用于处理多个文件,在读取每个文件的最后一条记录之后执行

注意

官方文档-The GNU Awk User’s Guide
https://www.gnu.org/software/gawk/manual/
https://www.gnu.org/software/gawk/manual/html_node/index.html
https://www.cnblogs.com/ginvip/p/6352157.html

名称说明:
行: row,record,记录
列: column,field,字段,域

awk命令只是过滤并格式化显示文件内容,并非更改文件内容
'program'命令处理语句使用单引号,"string"字符串使用双引号,(condition)条件判断语句用小括号表示

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
$ awk --version
GNU Awk 4.0.2

# 文本文件
$ cat file
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
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
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
usera:x:1000:1000::/home/usera:/bin/bash
userb:x:1001:1001::/home/userb:/bin/bash

# 显示文件内容,等同于cat命令
$ awk '{print $0}' file
# 过滤文件内容,等同于grep
$ awk '/bash/{print}' file
root:x:0:0:root:/root:/bin/bash
usera:x:1000:1000::/home/usera:/bin/bash
userb:x:1001:1001::/home/userb:/bin/bash
# -F 指定分隔符
$ awk -F: '/bash/{print $1}' file
root
usera
userb
# 指定多个文件,多个分隔符,如需指定多个用[]括起来,如[ ,]表示指定空格或逗号为分隔符
$ awk -F[:,] '{print $1}' file1 file2
# 显示指定字段1和字段3,用逗号分隔符默认显示为空格,使用双引号括起来则显示逗号本身
$ awk -F: '/bash/{print $1,$3}' file
root 0
usera 1000
userb 1001
$ awk -F: '/bash/{print $1","$3}' file
root,0
usera,1000
userb,1001
$ awk -F: '/bash/{print $1"="$3}' file
root=0
usera=1000
userb=1001
# -d 显示awk默认的全局变量,默认文件名awkvars.out
$ awk -d -F: '{print}' file
$ more awkvars.out
ARGC: 2
ARGIND: 1
ARGV: array, 2 elements
BINMODE: 0
CONVFMT: "%.6g"
ERRNO: ""
FIELDWIDTHS: ""
FILENAME: "file"
FNR: 15
FPAT: "[^[:space:]]+"
FS: ":"
IGNORECASE: 0
LINT: 0
NF: 7
NR: 15
OFMT: "%.6g"
OFS: " "
ORS: "\n"
RLENGTH: 0
RS: "\n"
RSTART: 0
RT: "\n"
SUBSEP: "\034"
TEXTDOMAIN: "messages"
# 默认正则匹配大小写
$ cat file
aaaa
aaAA
AA BB
aAAa
aa bb
$ awk '/aa/{print}' file
aaaa
aaAA
aa bb
# 配置IGNORECASE=1忽略大小写匹配,建议使用BEGIN{IGNORECASE=1};
$ awk '/aa/{IGNORECASE=1;print}' file
$ awk 'BEGIN{IGNORECASE=1};/aa/{print}' file
aaaa
aaAA
AA BB
aAAa
aa bb
# 默认匹配单词大小写
$ awk '/\<aa\>/{print}' file
aa bb
# 忽略单词大小写
$ awk 'BEGIN{IGNORECASE=1};/\<aa\>/{print}' file
AA BB
aa bb

# $NF表示最后一个字段,$(NF-1)表示倒数第2个字段
$ awk -F: '/bash/{print $NF}' file
/bin/bash
/bin/bash
/bin/bash
$ awk -F: '/bash/{print $(NF-1)}' file
/root
/home/usera
/home/userb
# NR显示行号,$RS表示每行的内容,NF表示字段数量,注意加不加$符号是有区别的
$ awk -F: '/bash/{print NR,$RS}' file
1 root:x:0:0:root:/root:/bin/bash
14 usera:x:1000:1000::/home/usera:/bin/bash
15 userb:x:1001:1001::/home/userb:/bin/bash
$ awk -F: '/bash/{print NR,NF}' file
1 7
14 7
15 7
$ awk -F: '/bash/{print NR,$1}' file
1 root
14 usera
15 userb
# 不显示第1列$1,即删除字段1,默认分隔符为空格
$ awk -F: '/bash/{$1="";print}' file
x 0 0 root /root /bin/bash
x 1000 1000 /home/usera /bin/bash
x 1001 1001 /home/userb /bin/bash
# 不显示第1列,并指定原始分隔符
$ awk -F: '/bash/{OFS=":";$1="";print}' file
:x:0:0:root:/root:/bin/bash
:x:1000:1000::/home/usera:/bin/bash
:x:1001:1001::/home/userb:/bin/bash
# 使用BEGIN语句指定FS和OFS
$ awk 'BEGIN{FS=":";OFS=","}/bash/{print $1,$3,$6}' file
root,0,/root
usera,1000,/home/usera
userb,1001,/home/userb
$ awk 'BEGIN{FS=":";OFS=",";print "### BEGIN ###"};/bash/{print $1,$3,$6};END{print "### END ###"}' file
### BEGIN ###
root,0,/root
usera,1000,/home/usera
userb,1001,/home/userb
### END ###
# 使用system()内建函数执行linux命令
$ awk 'BEGIN{FS=":";OFS=",";print "### BEGIN ###";system("date")}/bash/{print $1,$3,$6}END{print"### END ###"}' file
# 显示最后一行的字段数
$ awk -F: 'END{print NF}' file
7
# 显示文件的行数
$ awk -F: 'END{print NR}' file
15
# awk执行linux命令,并显示执行结果,推荐使用system()
$ awk 'BEGIN{system("ls -al")}'
$ awk 'BEGIN{system("date")}'
$ awk 'BEGIN{"date"|getline;print}'
$ awk 'BEGIN{"date"|getline d;print d}'
# 显示时间
$ awk 'BEGIN{print systime()'
$ awk 'BEGIN{print strftime()}'
$ awk 'BEGIN{print strftime("%F-%T")}'
# ! 表示取反,&&表示与,||表示或
$ awk -F: '!/bash/{print NR,$1}' file
2 bin
3 daemon
4 adm
5 shutdown
6 halt
7 mail
8 nobody
9 systemd-network
10 polkitd
11 sshd
12 postfix
13 chrony
# 匹配user和bash,以下两条命令等同
$ awk -F: '/user.*bash/ {print NR,$1}' file
$ awk -F: '/user/&&/bash/ {print NR,$1}' file
14 usera
15 userb
# 匹配user或bash,以下两条命令等同
$ awk -F: '/user|bash/ {print NR,$1}' file
$ awk -F: '/user/||/bash/ {print NR,$1}' file
1 root
14 usera
15 userb
# -v 自定义变量,也可以修改内建变量,以下两条命令等同
$ awk -v FS=: -v OFS=',' '{print $1,$3,$7}' file
$ awk -F: '{print $1","$3","$7}' file
root,0,/bin/bash
bin,1,/sbin/nologin
# 指定输出记录分隔符ORS,默认为换行符
$ awk -F: -v ORS=';' '{print $1}' file
root;bin;daemon;adm;shutdown;
# 自定义变量显示
$ awk -v a="hello,awk" 'BEGIN{print a}'
hello,awk
$ awk 'BEGIN{a="hello,awk";print a}'
hello,awk

# 使用printf格式化输出
$ awk -F: '/bash/{printf "%s ",$1}' file
root usera userb
$ awk -F: '/bash/{printf "%s\n",$1}' file
root
usera
userb
$ awk -F: '/bash/{printf "%-10s %5d\n",$1,$3}' file
root 0
usera 1000
userb 1001
$ awk -F: '/bash/{printf "username: %s\n",$1}' file
username: root
username: usera
username: userb
$ awk 'BEGIN{a="2222.2222";printf "%f\n",a}'
2222.222200
$ awk 'BEGIN{a="2222.2222";printf "%.2f\n",a}'
2222.22
$ awk 'BEGIN{a="2222.2222";printf "%8.2f\n",a}'
2222.22

# 运算符
$ awk 'BEGIN{print 3+2}'
5
$ awk 'BEGIN{print 3*2}'
6
$ awk 'BEGIN{print 2^10}'
1024
$ awk 'BEGIN{a=3;b=4;print a+b}'
7
# 匹配包含user的行并显示字段1
$ awk -F: '$0~/user/{print $1}' file
usera
userb
# 显示字段3的值等于0的行
$ awk -F: '$3==0' file
root:x:0:0:root:/root:/bin/bash
$ awk -F: '$3==0 || $3>=1000' file
root:x:0:0:root:/root:/bin/bash
usera:x:1000:1000::/home/usera:/bin/bash
userb:x:1001:1001::/home/userb:/bin/bash
$ awk -F: '$3>=500 && $3<1000' file
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
# ?: 表达式, 如下匹配不包括nologin的行,如果$3>=1000则显示$1字段,否则显示$3字段
$ awk -F: '!/nologin/{print ($3>=1000?$1:$3)}' file
0
6
7
usera
userb
$ id 6
uid=6(shutdown) gid=0(root) groups=0(root)
$ id 7
uid=7(halt) gid=0(root) groups=0(root)
# 匹配不包括nologin的行,然后再匹配bash,如果行中包括bash则显示$1字段,否则显示最后一个字段$NF
$ awk -F: '!/nologin/{print /bash/?$1:$NF}' file
root
/sbin/shutdown
/sbin/halt
usera
userb
# 匹配不包括nologin的行和包括bash的行
$ awk -F: '!/nologin/||/bash/{print $3>=1000?$1:$NF}' file
/bin/bash
/sbin/shutdown
/sbin/halt
usera
userb
# 控制语句if
$ awk -F: '{if($NF~/bash/)print $1}' file
root
usera
userb
$ awk -F: '{if($NF~/bash/) print $1,$NF}' file
$ awk -F: '($NF~/bash/){print $1,$NF}' file
root /bin/bash
usera /bin/bash
userb /bin/bash
$ awk -F: '{if($3<1000) print "system user";else print "normal user"}' file
system user
...
normal user
normal user
$ awk -F: '{if($3!=$4)print $1,$3,$4}' file
adm 3 4
shutdown 6 0
halt 7 0
mail 8 12
polkitd 999 998
chrony 998 996
# for循环和while循环
$ awk 'BEGIN{for(i=1;i<=10;i++) print i}'
$ awk 'BEGIN{while(x<=10){++x;print x}}'
$ awk 'BEGIN{while(x<=10) ++x;print x}'
$ awk 'BEGIN{do {++x;print x} while (x<=10)}'

# 显示指定范围内的列的内容,如下显示从第3列到最后一列的内容
$ cat file
2021-11-30 10:58 sudo cp Downloads/MY_CA.crt /usr/local/share/ca-certificates
$ awk '{for (i=3;i<=NF;i++) printf ("%s ",$i);print ""}' file
sudo cp Downloads/MY_CA.crt /usr/local/share/ca-certificates

# 内建函数int()显示整数
$ awk 'BEGIN{print int(22.22)}'
22
# 显示每行的字符数
$ awk -F: '{print length()}' file
31
32
39
# 显示字段1的字符数
$ awk -F: '{print length($1)}' file
4
3
6
# 替换匹配到的第一个user为name,类似于sed s/user/name/
$ awk -F: 'sub(/user/,"name"){print}' file
namea:x:1000:1000::/home/usera:/bin/bash
nameb:x:1001:1001::/home/userb:/bin/bash
# 替换所有匹配到的user为name,类似于sed s/user/name/g
$ awk -F: 'gsub(/user/,"name"){print}' file
namea:x:1000:1000::/home/namea:/bin/bash
nameb:x:1001:1001::/home/nameb:/bin/bash
# 替换字段6
$ awk -F: 'sub(/user/,"name",$6){print}' file
usera x 1000 1000 /home/namea /bin/bash
userb x 1001 1001 /home/nameb /bin/bash
$ awk -F: 'sub(/user/,"name",$6){OFS=":";print}' file
usera:x:1000:1000::/home/namea:/bin/bash
userb:x:1001:1001::/home/nameb:/bin/bash
$ awk -F: 'sub(/user/,"name",$6){print $1,$6}' file
usera /home/namea
userb /home/nameb
# 转换为大写字母
$ awk -F: '{print toupper($1)}' file
ROOT
BIN
DAEMON
# 转换为大写字母
$ awk -F: '{print tolower($1)}' file
# 显示匹配到的行,,以下两条命令等同
$ awk -F: '/bash/{print $0}' file
$ awk -F: '/bash/{print substr($0,1)}' file
root:x:0:0:root:/root:/bin/bash
usera:x:1000:1000::/home/usera:/bin/bash
userb:x:1001:1001::/home/userb:/bin/bash
# 显示字段1的前5个字符
$ awk -F: '/bash/{print substr($1,1,5)}' file
root
usera
userb
# 显示字段1的第2-4个字符
$ awk -F: '/bash/{print substr($1,2,4)}' file
oot
sera
serb
# 显示字段1的第3个字符之后的内容
$ awk -F: '/bash/{print substr($1,3)}' file
ot
era
erb
$ cat a
root,usera,userb,
root,usera,userb.
# 删除每行最后一个字符
$ awk '{print substr($0, 1, length($0)-1)}' a
root,usera,userb
root,usera,userb
# 删除字段1的最后一个字符
$ awk -F, '{print substr($1, 1, length($1)-1)}' a
roo
roo
# 使用sub函数删除最后一个字符
$ awk '{sub(/.$/,""); print}' a
root,usera,userb
root,usera,userb
$ awk -F: '/bash/{ORS=",";print $1}' file
root,usera,userb,
# 使用head或sed删除最后一个字符-推荐
$ awk -F: '/bash/{ORS=",";print $1}' file |head -c-1
root,usera,userb
$ awk -F: '/bash/{ORS=",";print $1}' file |sed 's/.$//'
root,usera,userb

# awk也可以作为脚本执行
$ cat awk_print.awk
#!/usr/bin/awk -f
BEGIN{printf "%s\n","hello,awk"}
$ awk -f awk_print.awk
hello,awk
# 也可以加执行权限,直接执行
$ chmod 755 awk_print.awk
$ ./awk_print.awk
hello,awk

$ cat awk_passwd.awk
#!/usr/bin/awk -f
BEGIN{FS=":"}
/bash/{print $1,$3}
$ awk -f awk_passwd.awk file
root 0
usera 1000
userb 1001

# 显示磁盘使用率
$ df |grep "^/dev/sd" | awk '{print $5}'
15%
$ df |grep "^/dev/sd" | awk '{print int($5)}'
15

# awk调用shell变量
$ cat a.txt
12,a,like
23,b,can
289,c,like
1832,j,word
56,b,asdi-like
$ grep -w like a.txt | grep ',like$'
12,a,like
289,c,like
# 匹配指定列的字段并格式化显示
$ awk -F, '$3~/^like$/{print $1}' a.txt
12
289
# 更改显示格式
$ awk -F, '$3~/^like$/{ORS=",";print $1}' a.txt | head -c-1
12,289
# 设置shell变量,并通过awk调用,注意变量必须使用''括起来
$ word=like;awk -F, '$3~/^'$word'$/{ORS=",";print $1}' a.txt | head -c-1
12,289
$ word=word;awk -F, '$3~/^'$word'$/{ORS=",";print $1}' a.txt | head -c-1
1832
# 如果字符串包括单引号'则需要转义\',且字符串必须使用''括起来
$ awk -F, '$3~/^'like\'d'$/{print $1}' a.txt