Linux文本处理三剑客之awk学习笔记06:输出操作

  • A+
所属分类:linux技术
摘要

awk可以通过print或者printf将数据输出到标准输出或者重定向到文件中。print我们已经使用过很多次了。其实它本质是一个输出函数,即有小括号。


输出操作

awk可以通过print或者printf将数据输出到标准输出或者重定向到文件中。

print

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 %dn","alongdidi",29}' My name is alongdidi,My age is 29

如果format当中没有任何占位符的话,那么直接打印format字面内容,format后续的所有item都可以无视。

# awk 'BEGIN{printf "alongdidin",29,"male","single"}' alongdidi

常见的占位符(%+控制字母)。

%c:根据编码表(ASCII)打印字符。

# awk 'BEGIN{printf "%cn",65}' A

%d, %i:打印十进制整数,如果遇到小数则直接丢弃小数部分保留整数部分。

# awk 'BEGIN{printf "%dn%dn",30,30.3}' 30 30

%e, %E:使用科学计数法打印一个数字。

# awk 'BEGIN{printf "%en",10000000000}' 1.000000e+10

%f:打印浮点数(小数)。小数位足够长时会取近似值。

# awk 'BEGIN{printf "%fn",3.1415926}' 3.141593

%g, %G:使用科学计数法或者浮点数来显示,具体使用哪种取决于这两种表示法谁占用的字符数较少。

# awk 'BEGIN{printf "%gn",3.14}' 3.14 # awk 'BEGIN{printf "%gn",33333333333333333333333.14}' 3.33333e+22

%o:将十进制数转换成八进制数并以字符串格式打印出来。

# awk 'BEGIN{printf "%on",8}' 10

%x:将十进制数转换成十六进制数并以字符串格式打印出来。

# awk 'BEGIN{printf "%xn",16}' 10

%s:打印字符串。

%%:打印百分号。

# awk 'BEGIN{printf "%d%%n",100}' 100%

还可以使用修饰符,修饰符位于%和控制字母之间。

N$:N是一个整数。默认情况下各item按位(出现次序)与format中的占位符替换。而该修饰符可以改变这个顺序。

# awk 'BEGIN{printf "%s %s %sn","I","love","u"}' I love u # awk 'BEGIN{printf "%3$s %2$s %1$sn","I","love","u"}' u love I

可重复。

# awk 'BEGIN{printf "%2$s %2$s %2$sn","I","love","u"}' love love love

width:指定最短字符宽度。宽度不足时使用空格(这里使用下划线“_”表示)在字符串左边填充,超出宽度则该修饰符无效。

# awk 'BEGIN{printf "%4sn","foo"}' _foo # awk 'BEGIN{printf "%4sn","foobar"}' foobar

-:减号。默认情况下字符串右对齐(所以空格在左边填充),该修饰符使其左对齐。

# awk 'BEGIN{printf "%5sn%-5sn","ni","ni"}' ___ni ni___

space:空格。针对于数值表示其正负性。如果是正数在其前添加一个空格,对于负数则不改变。

# awk 'BEGIN{printf "% dn% dn",1,-1}' _1 -1

+:加号。与空格功能相同,使用正负号来表示正负数。

# awk 'BEGIN{printf "%+dn%+dn",1,-1}' +1 -1

#:换一种表示形式,例如在八进制前加上“0”,在十六进制前加上“0x”。

# awk 'BEGIN{printf "%#on%#xn",8,16}' 010 0x10

0:数字0,当需要使用空格填充时以零代替,针对数字。0只会在确保不会改变数字值大小的情况才使用。

# awk 'BEGIN{printf "%05dn",3}' 00003 # awk 'BEGIN{printf "%-05dn",3}' 3    # 在左对齐情况下,如果在数字3的右边补齐0会改变数字值大小。

':单引号。当系统的区域设置(locale)支持时,可以以千分位表示法表示。这里需要注意为了使单引号修饰符正确被解释,原本awk的单引号用双引号替代,原本printf的双引号使用反斜线转义。

# awk 'BEGIN{printf "%dn",1000000000}' 1000000000 # awk "BEGIN{printf "%'dn",1000000000}" 1,000,000,000 # LC_ALL=C awk "BEGIN{printf "%'dn",1000000000}" 1000000000

.prec:一个小数点后面跟着一个非负整数来表示精度(precision),不同的控制字符下精度有不同的含义。

%d, %i, %o, %x, %X:至少打印多少个数字。不够以0填充,超出没事。

# awk 'BEGIN{printf "%.5dn",300}' 00300 # awk 'BEGIN{printf "%.5dn",300000}' 300000

%e, %E, %f, %F:小数点后保留多少位小数。超出则取近似值,不够则以0填充。

# awk 'BEGIN{printf "%.3fn",3.1415926}' 3.142 # awk 'BEGIN{printf "%.9fn",3.1415926}' 3.141592600

%s:指定最长字符宽度,超出部分丢弃。

# awk 'BEGIN{printf "%.3sn","alongdidi"}' alo # awk 'BEGIN{printf "%.3sn","a"}' a

%g, %G:最大有效数字位数(整数+小数)。超出部分会丢弃,如何判断丢弃的部分取决于如何丢弃会使得结果更接近于原值。

# awk 'BEGIN{printf "%.3gn",3.1415926}' 3.14 # awk 'BEGIN{printf "%.3gn",333.14}' 333 # awk 'BEGIN{printf "%.3gn",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中读取数据