处理文本文件,经常会遇到反向输出的要求。
可用命令rev对待处理的文件或标准输入快速完成。
可用命令tac对文件快速完成反向查看。
而对行中字符串(单词)可借助其他命令达到反向输出的目标。
我们在文章《Linux CentOS7sed的替换及逆转功能》讨论了sed流编辑器对此三类反转要求的处理。
作为文本文件处理的利器,awk处理此类问题也是驾轻就熟。本文作一初步讨论,供参考。
文件由众多行构成,而行又可以分隔为多个域。
设计awk模式与动作,对于待处理对象操作、处理、组合、格式化输出等!
定义了大量内置变量,大大丰富了处理手段。
语法简单明确,三大块:
BEGIN
'模式匹配{命令执行} '
END
awk命令是由模式和操作组成的:
pattern {action} 如 awk '/root/' /etc/passwd
两者是可选的,如果没有模式,则action应用到全部记录,如果没有action,则输出匹配全部记录。默认情况下,每一个输入行都是一条记录,但用户可通过RS变量指定不同的分隔符进行分隔。
模式
模式可以是以下任意一个:
/正则表达式/: 使用通配符的扩展集。
关系表达式: 可以用下面运算符表中的关系运算符进行操作,可以是字符串或数字的比较,如$2>%1选择第二个字段比第一个字段长的行。
模式匹配表达式:用运算符~(匹配)和~!(不匹配)。
模式,模式: 指定一个行的范围。该语法不能包括BEGIN和END模式。
BEGIN: 让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。
END: 让用户在最后一条输入记录被读取之后发生的动作。
操作
操作由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内。主要有四部份:
变量或数组赋值
输出命令
内置函数
控制流命令
对于字符串按字符进行反向输出,常用命令是rev
echo 12345|rev
我们在文章《Linux centos7 bash中字符串反向输出》讨论了字符串如何反向输出。
而作为操作文本文件功能强大的awk命令,处理此问题也是非常简单的。主要是利用子串截取命令substr($0,i,1),从需处理的字符串中从左向右一次截取一个字符,放入新字符串,再打印输出,就达到反向输出效果。
代码
echo ABCD | awk '{for(i=1;i<=length;i++) {line=substr($0,i,1) line}} END{print line}'
诠释
1)substr($i,0,1)表示取当前字符从索引i开始,取当前位
2)length 为当前字符串的长度
3)line=substr($i,0,1) line 将三个值分别保存在内存栈中
>substr($3,6,2) 表示从第三个字段里的第六个字符开始。截取两个字符结束
>substr($3,6) 表示从第三个字段里的第六个字符开始,直到结束
对于字符串,我们可以认为是用''分隔的文本数据,重新设置内置分隔符FS,再反向循环,以字符串格式化输出
echo "helloworld"|awk 'BEGIN{FS = ""}{for(i = NF; i >= 1; i--) {printf("%s", $i)}{printf("\n")}}'
dlrowolleh ## 达到逆向输出效果
通过设置内置变量FS、OFS及ORS,达到利用tac反向输出行
echo abcde|awk '$1=$1' FS= OFS='\n'|tac|awk '$1=$1' ORS="";echo
其中 最后的echo是添加一个换行
按域号或列号(NF)递减输出
一次循环一个字符串,共循环NF次。
echo "5e 4d 3c 2b 1a"|awk '{for(i=NF;i>1;i--)printf ("%s ",$i);print $1}'
解析
这是最常用的命令,利用NF的降序输出,把最后一个域作为第一个输出,然后自减,最后输出$1,这里要注意的地方是printf,不能用print,因为print默认的ORS是换行,最后用print $1单独输出,既换行,又不会多个空格。
在反向输出时,先对调左右字符串,仅循环NF/2次
echo "4d 3c 2b 1a"|awk '{for(i=1;i<=NF/2;i++){t=$i;$i=$(NF+1-i);$(NF+1-i)=t}}1'
这是效率最高的一种办法,非常科学的算法,相当于把$1和$4对换,$2和$3对换。
for(i=1;i<=2;i++){t=$1;$1=$4;$4=t}
for(i=2;i<=2;i++){t=$2;$2=$3;$3=t}
这样就完成了$1和$4,$2和$3的对调
echo "I love linux and windows"|xargs -n1|tac|awk '$1=$1' ORS=" ";echo
按行号NR递减输出
awk '{line[NR]=$0}END{for(i=NR;i>0;i--)print line[i]}' passwd1
此方法利用数组功能,与while循环同样思路。
awk '{line[NR]=$0};END{i=NR;while(i>0){print line[i];i=i-1}}' passwd1