awk进阶
变量名 | 描述 |
---|---|
FS | 输入字段分隔符,默认是空格或制表符 |
OFS | 输出字段分隔符,默认是空格 |
RS | 输入记录分隔符,默认是换行符\n |
ORS | 输出记录分隔符,默认是换行符\n |
NF | 统计当前记录中字段个数 |
NR | 统计记录编号,每处理一行记录,编号就会+1 |
FNR | 统计记录编号,每处理一行记录,编号也会+1,与 NR 不同的是,处理第二个文件时,编号会重新计数。 |
ARGC | 命令行参数数量 |
ARGV | 命令行参数数组序列数组,下标从 0 开始,ARGV[0]是 awk |
ARGIND | 当前正在处理的文件索引值。第一个文件是 1,第二个文件是 2,以此类推 |
ENVIRON | 当前系统的环境变量 |
FILENAME | 输出当前处理的文件名 |
IGNORECASE | 忽略大小写 |
SUBSEP | 数组中下标的分隔符,默认为"\034" |
示例:
FS 和 OFS
在程序开始前重新赋值 FS 变量,改变默认分隔符为冒号,与-F 一样。
# awk 'BEGIN{FS=":"}{print $1,$2}' /etc/passwd |head -n5
root x
bin x
daemon x
adm x
lp x
也可以使用-v 来重新赋值这个变量
# awk -vFS=':' '{print $1,$2}' /etc/passwd |head -n5 # 中间逗号被换成了 OFS 的默
认值
root x
bin x
daemon x
adm x
lp x
由于 OFS 默认以空格分隔,反向引用多个字段分隔的也是空格,如果想指定输出分隔符这样
# awk 'BEGIN{FS=":";OFS=":"}{print $1,$2}' /etc/passwd |head -n5
root:x
bin:x
daemon:x
adm:x
lp:x
也可以通过字符串拼接实现分隔
# awk 'BEGIN{FS=":"}{print $1"#"$2}' /etc/passwd |head -n5
root#x
bin#x
daemon#x
adm#x
lp#x
2)RS 和 ORS
RS 默认是\n 分隔每行,如果想指定以某个字符作为分隔符来处理记录:
# echo "www.baidu.com/user/test.html" |awk 'BEGIN{RS="/"}{print $0}'
www.baidu.com
user
test.html
RS 也支持正则
# seq -f "str%02g" 10 |sed 'n;n;a\-----' |awk 'BEGIN{RS="-+"}{print $1}'
str01
str04
str07
str10
将输出的换行符替换为+号
# seq 10 |awk 'BEGIN{ORS="+"}{print $0}'
1+2+3+4+5+6+7+8+9+10+
替换某个字符:
# tail -n2 /etc/services |awk 'BEGIN{RS="/";ORS="#"}{print $0}'
iqobject 48619#udp # iqobject
matahari 49000#tcp # Matahari Broker
NF
NF 是字段个数
# echo "a b c d e f" |awk '{print NF}'
6
打印最后一个字段:
# echo "a b c d e f" |awk '{print $NF}'
f
打印倒数第二个字段:
# echo "a b c d e f" |awk '{print $(NF-1)}'
e
排除最后两个字段:
# echo "a b c d e f" |awk '{$NF="";$(NF-1)="";print $0}'
a b c d
排除第一个字段:
# echo "a b c d e f" |awk '{$1="";print $0}'
b c d e f
NR 和 FNR
NR 统计记录编号,每处理一行记录,编号就会+1,FNR 不同的是在统计第二个文件时会重新计数。
打印行数
# tail -n5 /etc/services |awk '{print NR,$0}'
1 com-bardac-dw 48556/tcp # com-bardac-dw
2 com-bardac-dw 48556/udp # com-bardac-dw
3 iqobject 48619/tcp # iqobject
4 iqobject 48619/udp # iqobject
5 matahari 49000/tcp # Matahari Broker
打印总行数:
# tail -n5 /etc/services |awk 'END{print NR}'
5
打印第三行:
# tail -n5 /etc/services |awk 'NR==3'
iqobject 48619/tcp # iqobject
打印第三行第二个字段:
# tail -n5 /etc/services |awk 'NR==3{print $2}'
48619/tcp
打印前三行:
# tail -n5 /etc/services |awk 'NR<=3{print NR,$0}'
1 com-bardac-dw 48556/tcp # com-bardac-dw
2 com-bardac-dw 48556/udp # com-bardac-dw
3 iqobject 48619/tcp # iqobject
NR 和 FNR 的区别
# cat a
a
b
c
# cat b
c
d
e
# awk '{print NR,FNR,$0}' a b
1 1 a
2 2 b
3 3 c
4 1 c
5 2 d
6 3 e
可以看出 NR 每处理一行就会+1,而 FNR 在处理第二个文件时,编号重新计数。同时也知道 awk 处理
两个文件时,是合并到一起处理。
# awk 'FNR==NR{print $0"1"}FNR!=NR{print $0"2"}' a b
a1
b1
c1
c2
d2
e2
当 FNR==NR 时,说明在处理第一个文件内容,不等于时说明在处理第二个文件内容。
一般 FNR 在处理多个文件时会用到,下面会讲解。
ARGC 和 ARGV
ARGC 是命令行参数数量
ARGV 是将命令行参数存到数组,元素由 ARGC 指定,数组下标从 0 开始
# awk 'BEGIN{print ARGC}' 1 2 3
4
# awk 'BEGIN{print ARGV[0]}'
awk
# awk 'BEGIN{print ARGV[1]}' 1 2
1
# awk 'BEGIN{print ARGV[2]}' 1 2
2
ARGIND
ARGIND 是当前正在处理的文件索引值,第一个文件是 1,第二个文件是 2,以此类推,从而可以通
过这种方式判断正在处理哪个文件。
# awk '{print ARGIND,$0}' a b
1 a
1 b
1 c
2 c
2 d
2 e
# awk 'ARGIND==1{print "a->"$0}ARGIND==2{print "b->"$0}' a b
a->a
a->b
a->c
b->c
b->d
b->e
ENVIRON
ENVIRON 调用系统变量。
# awk 'BEGIN{print ENVIRON["HOME"]}'
/root
如果是设置的环境变量,还需要用 export 导入到系统变量才可以调用:
# awk 'BEGIN{print ENVIRON["a"]}'
# export a
# awk 'BEGIN{print ENVIRON["a"]}'
123
FILENAME
FILENAME 是当前处理文件的文件名。
# awk 'FNR==NR{print FILENAME"->"$0}FNR!=NR{print FILENAME"->"$0}' a b
a->a
a->b
a->c
b->c
b->d
b->e
忽略大小写
# echo "A a b c" |xargs -n1 |awk 'BEGIN{IGNORECASE=1}/a/'
A
a
等于 1 代表忽略大小写。
8.3.4 操作符
运算符 描述
(....) 分组
$ 字段引用
++ -- 递增和递减
+ - ! 加号,减号,和逻辑否定
* / % 乘,除和取余
+ - 加法,减法
| |& 管道,用于 getline,print 和 printf
< > <= >= != == 关系运算符
~ !~ 正则表达式匹配,否定正则表达式匹配
in 数组成员
&& || 逻辑 and,逻辑 or
?: 简写条件表达式:
expr1 ? expr2 : expr3
第一个表达式为真,执行 expr2,否则执行 expr3
= += -= *= /= %= ^= 变量赋值运算符
须知:
在 awk 中,有 3 种情况表达式为假:数字是 0,空字符串和未定义的值。
数值运算,未定义变量初始值为 0。字符运算,未定义变量初始值为空。
举例测试:
# awk 'BEGIN{n=0;if(n)print "true";else print "false"}'
false
# awk 'BEGIN{s="";if(s)print "true";else print "false"}'
false
# awk 'BEGIN{if(s)print "true";else print "false"}'
false
截取整数
# echo "123abc abc123 123abc123" |xargs -n1 | awk '{print +$0}'
123
0
123
# echo "123abc abc123 123abc123" |xargs -n1 | awk '{print -$0}'
-123
0
-123
感叹号
打印奇数行
# seq 6 |awk 'i=!i'
1
3
5
打印偶数行:
# seq 6 |awk '!(i=!i)'
2
4
6
读取第一行:i 是未定义变量,也就是 i=!0,!取反意思。感叹号右边是个布尔值,0 或空字符串为
假,非 0 或非空字符串为真,!0 就是真,因此 i=1,条件为真打印当前记录。
没有 print 为什么会打印呢?因为模式后面没有动作,默认会打印整条记录。
读取第二行:因为上次 i 的值由 0 变成了 1,此时就是 i=!1,条件为假不打印。
读取第三行:上次条件又为假,i 恢复初始值 0,取反,继续打印。以此类推…
可以看出,运算时并没有判断行内容,而是利用布尔值真假判断输出当前行。
不匹配某行
# tail /etc/services |awk '!/blp5/{print $0}'
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker
乘法和除法
# seq 5 |awk '{print $0*2}'
2
4
6
8
10
# seq 5 |awk '{print $0%2}'
1
0
1
0
1
打印偶数行:
# seq 5 |awk '$0%2==0{print $0}'
2
4
打印奇数行:
# seq 5 |awk '$0%2!=0{print $0}'
1
3
5
管道符使用
# seq 5 |shuf |awk '{print $0|