Linux文本处理三剑客之awk学习笔记06:输出操作
输出操作
awk可以通过print或者printf将数据输出到标准输出或者重定向到文件中。
print我们已经使用过很多次了。其实它本质是一个输出函数,即有小括号。
print (elem1,elem2,elem3...)
print elem1,elem2,elem3...
输出的每一个东西,我们可以称之为字段/元素,在书写时使用逗号分隔多个字段。一个print当中的所有字段组合起来就是一条记录。在输出的时候,print会使用预定义变量OFS来分隔多个字段,多个print语句的输出就是多条记录,它们之间使用预定义变量ORS来分隔。这也是为什么默认情况下,print输出的多个字段使用空格分隔,多个print语句的输出结果使用换行分隔的原因了。
# awk 'BEGIN{OFS=":";ORS="!!!\n";print "a","b","c";print "d","e","f"}'
a:b:c!!!
d:e:f!!!
# awk 'BEGIN{OFS=":";ORS="!!!\n";print("a","b","c");print("d","e","f")}'
a:b:c!!!
d:e:f!!!
每个输出的字段都会被print转换成字符串格式后输出,即便是纯数字。若不是纯数字,则要将输出的字段使用双引号包裹,否则会被认为是变量而进行变量替换。
# awk 'BEGIN{print abc}'
# awk 'BEGIN{print "abc"}'
abc
如果输出的内容包含特殊符号(例如重定向字符),则需要使用小括号包裹。
[root@c7-server ~]# ls -l 4
ls: cannot access 4: No such file or directory
[root@c7-server ~]# awk 'BEGIN{print 5>4}'
[root@c7-server ~]# ls -l 4
-rw-r--r-- 1 root root 2 Jan 9 20:28 4
[root@c7-server ~]# cat 4
5
[root@c7-server ~]# awk 'BEGIN{print(5>4)}'
1
print除了在输出数字时会将其转换为字符串再输出以外,当输出小数的时候,会按照预定义变量OFMT(Output ForMaT)定义的格式进行格式化后输出。该变量的值定义的格式与printf()定义的格式相同。格式化输出的格式在讲解printf()时会再详述。OFMT的默认值为“%.6g”表示整数部分+小数部分最多6位。
# awk 'BEGIN{print 3.12432623}' 3.12433
同理,可修改OFMT来修改print对于小数的输出行为。
# awk 'BEGIN{OFMT="%.2f";print 3.99989}'
4.00
# awk 'BEGIN{OFMT="%d";print 3.99989}'
3
# awk 'BEGIN{OFMT="%.0f";print 3.99989}'
4
printf
print的输出格式是由ORS和OFS所决定的,而printf是格式化输出,其输出格式由其自身所决定。
printf format,item1,item2,...
format:指定了要输出的格式,其中包含了多个“%+控制字母”的东西表示占位符,其会被format之后出现的各个item所依次(默认)替换。
# awk 'BEGIN{printf "My name is %s,My age is %d\n","alongdidi",29}'
My name is alongdidi,My age is 29
如果format当中没有任何占位符的话,那么直接打印format字面内容,format后续的所有item都可以无视。
# awk 'BEGIN{printf "alongdidi\n",29,"male","single"}'
alongdidi
常见的占位符(%+控制字母)。
%c:根据编码表(ASCII)打印字符。
# awk 'BEGIN{printf "%c\n",65}'
A
%d, %i:打印十进制整数,如果遇到小数则直接丢弃小数部分保留整数部分。
# awk 'BEGIN{printf "%d\n%d\n",30,30.3}' 30 30
%e, %E:使用科学计数法打印一个数字。
# awk 'BEGIN{printf "%e\n",10000000000}'
1.000000e+10
%f:打印浮点数(小数)。小数位足够长时会取近似值。
# awk 'BEGIN{printf "%f\n",3.1415926}' 3.141593
%g, %G:使用科学计数法或者浮点数来显示,具体使用哪种取决于这两种表示法谁占用的字符数较少。
# awk 'BEGIN{printf "%g\n",3.14}'
3.14
# awk 'BEGIN{printf "%g\n",33333333333333333333333.14}'
3.33333e+22
%o:将十进制数转换成八进制数并以字符串格式打印出来。
# awk 'BEGIN{printf "%o\n",8}' 10
%x:将十进制数转换成十六进制数并以字符串格式打印出来。
# awk 'BEGIN{printf "%x\n",16}' 10
%s:打印字符串。
%%:打印百分号。
# awk 'BEGIN{printf "%d%%\n",100}' 100%
还可以使用修饰符,修饰符位于%和控制字母之间。
N$:N是一个整数。默认情况下各item按位(出现次序)与format中的占位符替换。而该修饰符可以改变这个顺序。
# awk 'BEGIN{printf "%s %s %s\n","I","love","u"}'
I love u
# awk 'BEGIN{printf "%3$s %2$s %1$s\n","I","love","u"}'
u love I
可重复。
# awk 'BEGIN{printf "%2$s %2$s %2$s\n","I","love","u"}'
love love love
width:指定最短字符宽度。宽度不足时使用空格(这里使用下划线“_”表示)在字符串左边填充,超出宽度则该修饰符无效。
# awk 'BEGIN{printf "%4s\n","foo"}'
_foo
# awk 'BEGIN{printf "%4s\n","foobar"}'
foobar
-:减号。默认情况下字符串右对齐(所以空格在左边填充),该修饰符使其左对齐。
# awk 'BEGIN{printf "%5s\n%-5s\n","ni","ni"}'
___ni
ni___
space:空格。针对于数值表示其正负性。如果是正数在其前添加一个空格,对于负数则不改变。
# awk 'BEGIN{printf "% d\n% d\n",1,-1}'
_1
-1
+:加号。与空格功能相同,使用正负号来表示正负数。
# awk 'BEGIN{printf "%+d\n%+d\n",1,-1}' +1 -1
#:换一种表示形式,例如在八进制前加上“0”,在十六进制前加上“0x”。
# awk 'BEGIN{printf "%#o\n%#x\n",8,16}' 010 0x10
0:数字0,当需要使用空格填充时以零代替,针对数字。0只会在确保不会改变数字值大小的情况才使用。
# awk 'BEGIN{printf "%05d\n",3}'
00003
# awk 'BEGIN{printf "%-05d\n",3}'
3 # 在左对齐情况下,如果在数字3的右边补齐0会改变数字值大小。
':单引号。当系统的区域设置(locale)支持时,可以以千分位表示法表示。这里需要注意为了使单引号修饰符正确被解释,原本awk的单引号用双引号替代,原本printf的双引号使用反斜线转义。
# awk 'BEGIN{printf "%d\n",1000000000}'
1000000000
# awk "BEGIN{printf \"%'d\n\",1000000000}"
1,000,000,000
# LC_ALL=C awk "BEGIN{printf \"%'d\n\",1000000000}"
1000000000
.prec:一个小数点后面跟着一个非负整数来表示精度(precision),不同的控制字符下精度有不同的含义。
%d, %i, %o, %x, %X:至少打印多少个数字。不够以0填充,超出没事。
# awk 'BEGIN{printf "%.5d\n",300}'
00300
# awk 'BEGIN{printf "%.5d\n",300000}'
300000
%e, %E, %f, %F:小数点后保留多少位小数。超出则取近似值,不够则以0填充。
# awk 'BEGIN{printf "%.3f\n",3.1415926}'
3.142
# awk 'BEGIN{printf "%.9f\n",3.1415926}'
3.141592600
%s:指定最长字符宽度,超出部分丢弃。
# awk 'BEGIN{printf "%.3s\n","alongdidi"}'
alo
# awk 'BEGIN{printf "%.3s\n","a"}'
a
%g, %G:最大有效数字位数(整数+小数)。超出部分会丢弃,如何判断丢弃的部分取决于如何丢弃会使得结果更接近于原值。
# awk 'BEGIN{printf "%.3g\n",3.1415926}'
3.14
# awk 'BEGIN{printf "%.3g\n",333.14}'
333
# awk 'BEGIN{printf "%.3g\n",33333333.14}'
3.33e+07
sprintf
printf将格式化后的结果输出而sprintf将格式化后的结果返回。因此可以将返回值赋值给变量或者使用print将返回值输出。
sprintf是一个字符串函数。
# awk 'BEGIN{sprintf("%s","alongdidi")}'
# awk 'BEGIN{print sprintf("%s","alongdidi")}'
alongdidi
# awk 'BEGIN{a=sprintf("%s","alongdidi");print a}'
alongdidi
重定向输出
awk支持四种重定向输出。
1、覆盖重定向输出至文件。
print[f] "something" > "filename"
类似于bash中的覆盖重定向。如果文件不存在则创建,如果文件存在的话则先清空原文件的数据再重定向数据。
对于同一个文件,awk只在首次操作文件时才将其打开。因此,该重定向会将随后的数据追加至文件,这和bash的覆盖重定向机制不同。
# ls -l name.txt
ls: cannot access name.txt: No such file or directory
# awk 'NR>1{print $2 > "name.txt"}' a.txt
# cat name.txt
Bob
... ...
Bruce # 除了第一个$2,后续的每一个$2都是以追加的形式。
2、追加重定向输出至文件。
print[f] "something" >> "filename"
与覆盖重定向的区别在于,当文件存在时,追加重定向不会清空文件,每一条记录都追加。
# cat redirection.txt
abc
def
# 上面是测试文件,自行尝试执行对比区别。
awk 'BEGIN{for(i=1;i<=3;i++){print "alongdidi">>"redirection.txt"}}'
awk 'BEGIN{for(i=1;i<=3;i++){print "alongdidi">"redirection.txt"}}'
3、通过管道重定向输出其他命令。
print[f] "something" | shellCmd
示例。
awk 'NR>1{print $2>"name.unsort";cmd="sort>name.sort";print $2|cmd}END{close(cmd)}' a.txt
4、重定向输出给协程。
print[f] "something" |& shellCmd
至于重定向输入,我们在讲解getline时基本都有涉及到了。
stdin, stdout, stderr
awk在重定向时可以直接使用/dev/stdin、/dev/stdout和/dev/stderr。
awk 'BEGIN{print "something OK" > "/dev/stdout"}' awk 'BEGIN{print "something OK" > "/dev/stderr"}' awk 'BEGIN{getline<"/dev/stdin";print $0}' awk 'BEGIN{print "something OK" | "cat >&2"}'
使用文件描述符重定向数据。
exec 4<> a.txt # 为文件a.txt分配一个文件描述符4
awk 'BEGIN{while(getline<"/dev/fd/4"){print}}' # 从文件描述符4中读取数据