CU社区shell板块awk十三问整理

时间:2023-03-09 17:13:41
CU社区shell板块awk十三问整理

CU社区shell板块awk十三问整理

一、RS=""

当 RS="" 时,会将\n强制加入到FS变量中,因为RS为空时,是将连续多空行作为分隔符,近似于\n\n+,\n就不能作为行分隔符,所以此时awk默认将\n归为FS

[root@localhost ~]# cat urfile
1
a 2
a 3
[root@localhost ~]# awk -v RS="" '{print "#" $0 "#"}' urfile
#1
a#
#2
a#
#3#

若文件展示位一行,即在硬盘中存贮方式

1\na\n\n2\na\n\n3\n

将内容转化为熟悉的理解方式,RS=\n

1\na
2\na
3
  • 执行后的结果应该为
#1\na#
#2\na#
#3#
  • 实际结果展示
[root@localhost ~]# awk -v RS="" '{print "#" $0 "#"}' urfile
#1
a#
#2
a#
#3#
[root@localhost ~]# awk -v RS="" '{print "#" $1 "#"}' urfile
#1#
#2#
#3#
  • 如果RS被设置为空,那么awk会将连续的空行作为行分隔符,与RS设置成"\n\n+"有什么区别???
  1. 忽略文件开头和结尾的空行。且文件不以记录分隔符结束,即最后不是空行,会将最后一个记录的尾\n去掉
  2. 设置RT变量未空
  3. FS变量
总结下RS的3种情况:
  1. 非空字符串

    以固定字符串作为行分隔符,同时设置变量RT为固定字符串
  2. 正则表达式

    以正则表达式作为行分隔符,同时设置变量RT为正则表达式实际匹配到的字符串
  3. 空字符

    以连续的空行作为行分隔符,如果FS为单个字符,会将\n强制加入到FS变量中
理解了RS,再来理解ORS就简单了。RS是awk读取文件时的行分隔符,ORS则是awk输出时的行结束符。
更简单的讲,就是awk在输出时,会在每行记录后面增加一个ORS变量所设定的值。
ORS的值只能设定为字符串,默认情况下,ORS的值是\n
[root@localhost ~]# seq 5 | awk '{print $0}'
1
2
3
4
5
[root@localhost ~]# seq 5 | awk 'BEGIN{ORS="a"}{print $0}'
1a2a3a4a5a
我们平常用的 print $0 等价于 printf $0 ORS

二、FS=""

  • 当FS被设置为空字符串的时候,awk会将一行记录的每个字符做为单独的一列

  • 类似的,当我们想以固定的长度来分隔列的时候,可以使用 FIELDWIDTHS 来代替 FS

  • 例如,一行记录的前3个字符作为第一列,接下来的2个字符作为第二列,接下来的4个字符作为第三列

[root@localhost ~]# echo '123456789' | awk 'BEGIN{FIELDWIDTHS="3 2 4"}{print $1,$2,$3}'
123 45 6789
[root@localhost ~]# echo '123456789' | awk 'BEGIN{FIELDWIDTHS="3 2 3"}{print $1,$2,$3}'
123 45 678
[root@localhost ~]# echo '123456789' | awk 'BEGIN{FIELDWIDTHS="3 2 5"}{print $1,$2,$3}'
123 45 6789
总结下FS的4种情况:
  1. 非空字符串

    以固定字符串作为列分隔符
  2. 正则表达式

    以正则表达式作为列分隔符
  3. 单个空格

    以连续的 空格 或 制表符(\t) 或 换行符(\n)作为列分隔符
  4. 空字符

    以每个字符做为单独的一列

三、OFS

为了使OFS的设置生效,需要改变 $0 ,这里我们是对 awk 撒了个小谎

$1=$1 或者 NF+=0, $0 本身的内容实际上没有任何改变,只是为了使 OFS 的设置生效

这里执行$1=$1;只是为了欺骗awk,让awk认为对文本进行了修改,否则awk将输入结果直接输出不做修改

四、真假

其实要解释这个问题,只需要弄清楚awk中的“真”与“假”。

以下3种情况是“假”,其他情况都为“真”

  1. 数字 0
  2. 空字符串
  3. 未定义的值
  • awk 会根据语境来给未定义的变量赋初始值
[root@localhost ~]# awk 'BEGIN{print a "" 1}'
1
[root@localhost ~]# awk 'BEGIN{print a + 1}'
1

对于未定义的变量,如果要进行字符串操作,会被赋成空字符串 ""

如果要进行数学运算,会被赋成数字 0

现在我们看看上面的代码 ! a[$0] ++ 等价于 if(! a[$0] ++) print $0

对于首次出现的记录,a[$0]的值是未定义的,由于后面的 ++ 是数学计算,所以a[$0]会被赋值成数字0

也是由于 ++ 操作符,会先取值,再计算,所以对于第一行记录实际上是if(! 0) print $0

! 是取反,0 是假,! 0 就是真,那么就会执行后面的 print $0

对于后面出现的重复记录,a[$0] 经过 ++ 的计算已经变为 1、2、3 。。。

而 ! 1 ! 2 ! 3 ... 都为假,不会打印。

用awk打印奇数行:

[root@localhost ~]# seq 10 | awk 'i=!i'
1
3
5
7
9

awk中的赋值功能是先处理"="右边的内容(假设需要处理的话);

  • i变量没有赋值,为假。
    • 第一次执行!i则为真,将结果赋值给i;
    • 第二次执行!i则为假,将结果赋值给i;
    • 依次类推;真假循环
插一句  awk打印偶数行
awk 'i++%2'

五、一些awk内置变量

  1. 上面还有出现了一个变量FILENAME,这个变量的值就是awk当前处理的文件的文件名
  2. 这里我们还有几个变量需要了解:ARGV、ARGC、ARGIND
  3. ARGV 是一个数组,它记录着命令行的所有参数的值
  4. ARGC 是命令行参数的个数,(不包括-F、-v之类的awk参数)
  5. ARGIND 是ARGV数组的索引值,从0到ARGC-1

当我们想去了解这些变量时,最简单并且最有效的方法就是print

[root@localhost ~]# awk 'BEGIN{for(i=0;i<ARGC;i++) print "ARGV["i"]="ARGV[i]}{print ARGV[ARGIND],ARGIND,ARGC,$0}' [abc].txt
ARGV[0]=awk
ARGV[1]=a.txt
ARGV[2]=b.txt
ARGV[3]=c.txt
a.txt 1 4 a
a.txt 1 4 b
a.txt 1 4 c
b.txt 2 4 d
b.txt 2 4 e
b.txt 2 4 f
c.txt 3 4 g
c.txt 3 4 h
c.txt 3 4 i
在任何情况下,ARGV[ARGIND]始终是有值的,而FILENAME则不一定

六、awk的IO

awk中的IO就不得不提 getline,其实还有就是next跟nextfile

  • 这里主要说下getline

  • getline的用法很灵活,可以从awk当前处理的文件中获取下一行记录

  • 也可以从外部的文件或者管道中获取记录,每次只取一行,赋给$0或者变量var

  • 先来看个简单的例子,实现 grep -A1

[root@localhost ~]# seq 10 | grep -A1 5
5
6
[root@localhost ~]# seq 10 | awk '/5/{print;getline;print}'
5
6
  • 将两个输出或者说变量上每列分别合在一起的方法
[root@localhost ~]# var1='a
> b
> c
> d'
[root@localhost ~]# var2='A
> B
> C
> D'
[root@localhost ~]# export var2
[root@localhost ~]# echo "$var1" | awk '{"echo \042$var2\042"|getline var2;print $1""var2}'
aA
bB
cC
dD