85. 使用 printf 进行漂亮的打印


printf 非常灵活,允许您按照您想要的方式打印输出,从而使报告打印工作相对容易。

语法:

printf "print format", variable1, variable2, etc. 

以下是一些可以在 printf 中使用的特殊字符。

特殊字符 解释
\n 新行
\t 制表符
\v 垂直制表符
\b 退格键
\r 回车
\f 换页

以下使用换行符在单独的行中打印 "Line 1""Line 2"

$ awk 'BEGIN { printf "Line 1\nLine 2\n" }'
Line 1
Line 2

以下打印由制表符分隔的不同字段,其中 "Field 1" 后有 2 个制表符:

$ awk 'BEGIN { printf "Field 1\t\tField 2\tField 3\tField 4\n" }'
Field 1        Field 2        Field 3        Field 4

以下在每个字段后打印垂直制表符:

$ awk 'BEGIN { printf "Field 1\vField 2\vField 3\vField 4\n" }'
Field 1
    Field 2
        Field 3
            Field 4

以下代码在除 "Field 4" 之外的每个字段后打印一个退格键。 这将删除前三个字段中每个字段中的最后一个数字。例如 "Field 1" 显示为 "Field",因为最后一个字符被退格键删除。 然而,最后一个字段 "Field 4" 按原样显示,因为 "Field 4" 后面没有 \b

$ awk 'BEGIN { printf "Field 1\bField 2\bField 3\bField 4\n" }'
Field Field Field Field 4 

在下面的示例中,打印每个字段后,我们执行 "回车" 并在当前打印值之上打印下一个值。 这意味着,在最终输出中您看到的只是 "Field 4",因为它是在所有先前字段之上打印的最后一个内容。

$ awk 'BEGIN { printf "Field 1\rField 2\rField 3\rField 4\n" }'
Field 4

当您使用 print 命令(不是 printf)打印以逗号分隔的多个值时,它使用 OFS ORS 内置变量值来决定如何打印字段。

以下示例显示了简单的打印语句 "print $2,$3" 如何受到使用 OFS ORS 值的影响。

$ cat print.awk
BEGIN {
  FS=",";
  OFS=":";
  ORS="\n--\n";
}
{
  print $2,$3
}

$ awk -f print.awk items.txt
HD Camcorder:Video
--
Refrigerator:Appliance
--
MP3 Player:Audio
--
Tennis Racket:Sports
--
Laser Printer:Office
-- 

printf OFS ORS 变量。 它仅使用 printf命令的 "format" 字段中指定的内容,如下例所示。

$ cat printf1.awk
BEGIN {
  FS=",";
  OFS=":";
  ORS="\n--\n";
}
{
  printf "%s^^%s\n\n", $2, $3
}

$ awk -f printf1.awk items.txt
HD Camcorder^^Video
Refrigerator^^Appliance
MP3 Player^^Audio
Tennis Racket^^Sports
Laser Printer^^Office

printf 格式说明符。

格式字符 解释
s 字符串
c 单个字符
d 十进制
e 指数浮点
f 固定浮点数
g 使用 ef,具体取决于给定输入中较小的一个
o 八进制
x 十六进制
% 打印百分比符号

以下示例显示了格式说明符的基本用法:

$ cat printf-format.awk
BEGIN {
  printf "s--> %s\n", "String"
  printf "c--> %c\n", "String"
  printf "s--> %s\n", 101.23
  printf "d--> %d\n", 101.23
  printf "e--> %e\n", 101.23
  printf "f--> %f\n", 101.23
  printf "g--> %g\n", 101.23
  printf "o--> %o\n", 0x8
  printf "x--> %x\n", 16
  printf "percentage--> %%\n", 17
}

$ awk -f printf-format.awk
s--> String
c--> S
s--> 101.23
d--> 101
e--> 1.012300e+02
f--> 101.230000
g--> 101.23
o--> 10
x--> 10
percentage--> % 

要创建固定列宽报告,您必须在格式说明符中的 % 之后指定一个数字。该数字表示要打印的最小字符数。当输入字符串小于指定数量时,会在左侧添加空格以使其固定宽度。

以下示例显示 printf 语句的基本用法,并在%% 后立即指定数字

$ cat printf-width.awk
BEGIN {
  FS=","
  printf "%3s\t%10s\t%10s\t%5s\t%3s\n","Num","Description","Type","Price","Qty"
  printf "-----------------------------------------------------\n"
}
{
  printf "%3d\t%10s\t%10s\t%g\t%d\n", $1,$2,$3,$4,$5
}

$ awk -f printf-width.awk items.txt
Num	Description	      Type	Price	Qty
-----------------------------------------------------
101	HD Camcorder	     Video	210	10
102	Refrigerator	 Appliance	850	2
103	MP3 Player	     Audio	270	15
104	Tennis Racket	    Sports	190	20
105	Laser Printer	    Office	475	5

注意:请注意,即使我们指定了确切的宽度,输出可能还是有点参差不齐。这是因为我们指定的宽度实际上是最小宽度,而不是绝对尺寸; 如果输入字符串的字符数多于该字符数,则将打印整个字符串。因此,您应该真正注意要打印多少个字符。

如果即使输入字符串比指定的数字长,您也想打印固定的列宽,则应使用 substr 函数(或)在格式标识符中的数字前添加小数(稍后解释)。

在前面的示例中,第二个字段比指定的 10 个字符宽度宽,因此结果不是预期的结果。

左侧添加空格以将 "Good" 打印为 6 个字符的字符串:

$ awk 'BEGIN { printf "%6s\n", "Good" }'
  Good

即使您指定了 6 个字符宽度,也会在此处打印整个字符串:

$ awk 'BEGIN { printf "%6s\n", "Good Boy!" }'
Good Boy!

当输入字符串小于指定的字符数,并且您希望它左对齐(通过在右侧补充空格)时,请在 % 后面和数字之前立即使用减号 (-)。

"%6s" 右对齐,如下所示:

$ awk 'BEGIN { printf "|%6s|\n", "Good" }'
|  Good| 

"%-6s" 左对齐,如下所示:

$ awk 'BEGIN { printf "|%-6s|\n", "Good" }'
|Good  | 

要在价格值之前添加美元符号,只需在 printf 中的标识符之前添加美元符号,如下所示。

$ cat printf-width2.awk
BEGIN {
  FS=","
  printf "%-3s\t%-10s\t%-10s\t%-5s\t%-3s\n","Num","Description","Type","Price","Qty"
  printf "-----------------------------------------------------\n"
}
{
  printf "%-3d\t%-10s\t%-10s\t$%-.2f\t%-d\n",$1,$2,$3,$4,$5
}

$ awk -f printf-width2.awk items.txt
Num	Description	Type      	Price	Qty
-----------------------------------------------------
101	HD Camcorder	Video     	$210.00	10
102	Refrigerator	Appliance 	$850.00	2
103	MP3 Player	Audio     	$270.00	15
104	Tennis Racket	Sports    	$190.00	20
105	Laser Printer	Office    	$475.00	5

默认情况下,值右对齐,并在左侧添加空格。

$ awk 'BEGIN { printf "|%5s|\n", "100" }'
|  100|

对于右对齐且数字前面有 0(而不是空格),请在数字前添加零 (0)。 即使用 "%05s"``` 作为格式标识符,而不是 "%5s"```。

$ awk 'BEGIN { printf "|%05s|\n", "100" }'
|00100|

以下示例对数量字段使用前导零格式标识符。

$ cat printf-width3.awk
BEGIN {
  FS=","
  printf "%-3s\t%-10s\t%-10s\t%-5s\t%-3s\n","Num","Description","Type","Price","Qty"
  printf "-----------------------------------------------------\n"
}
{
  printf "%-3d\t%-10s\t%-10s\t$%-.2f\t%03d\n",$1,$2,$3,$4,$5
}

$ awk -f printf-width3.awk items.txt
Num	Description	Type      	Price	Qty
-----------------------------------------------------
101	HD Camcorder	Video     	$210.00	010
102	Refrigerator	Appliance 	$850.00	002
103	MP3 Player	Audio     	$270.00	015
104	Tennis Racket	Sports    	$190.00	020
105	Laser Printer	Office    	$475.00	005

正如我们已经向您展示的,当输入字符串包含的字符多于格式说明符中指定的字符时,它会打印整个内容,如下所示。

$ awk 'BEGIN { printf "%6s\n", "Good Boy!" }'
Good Boy!

要打印最多 6 个字符,请在数字前添加小数点。即,使用 "%.6s" 代替 "%6s",即使输入字符串比下面所示的长度长,它也只会打印输入字符串中的 6 个字符。

$ awk 'BEGIN { printf "%.6s\n", "Good Boy!" }'
Good B

上述内容并不适用于 awk 的所有版本。在 GAWK 3.1.5 上它有效。 但在 GAWK 3.1.7 上它不起作用。

因此,打印固定字符的可靠方法可能是使用 substr 函数,如下所示。

$ awk 'BEGIN { printf "%6s\n", substr("Good Boy!",1,6) }'
Good B

格式标识符中数字前面的点表示精度。

以下示例显示了数字格式标识符的数字前的点的工作原理。 此示例显示了使用 using .1.4(使用 defg 格式说明符)时如何以不同方式打印数字 "101.23"

$ cat dot.awk
BEGIN {
  print "----Using .1----"
  printf ".1d--> %.1d\n", 101.23
  printf ".1e--> %.1e\n", 101.23
  printf ".1f--> %.1f\n", 101.23
  printf ".1g--> %.1g\n", 101.23
  print "----Using .4----"
  printf ".4d--> %.4d\n", 101.23
  printf ".4e--> %.4e\n", 101.23
  printf ".4f--> %.4f\n", 101.23
  printf ".4g--> %.4g\n", 101.23
}

$ awk -f dot.awk
----Using .1----
.1d--> 101
.1e--> 1.0e+02
.1f--> 101.2
.1g--> 1e+02
----Using .4----
.4d--> 0101
.4e--> 1.0123e+02
.4f--> 101.2300
.4g--> 101.2

您可以将 print 语句的输出重定向到 awk 脚本内的特定输出文件。

在以下示例中,第一个打印语句具有 ">report.txt",它创建 report.txt 文件并将打印语句的输出发送给该文件。 所有后续打印语句都有 ">>report.txt",它将输出附加到现有的 report.txt 文件中。

$ cat printf-width4.awk
BEGIN {
  FS=","
  printf "%-3s\t%-10s\t%-10s\t%-5s\t%-3s\n","Num","Description","Type","Price","Qty" > "report.txt"
  printf "-----------------------------------------------------\n" >> "report.txt"
}
{
  if ($5 > 10)
    printf "%-3d\t%-10s\t%-10s\t$%-.2f\t%03d\n",$1,$2,$3,$4,$5 >> "report.txt"
}

$ awk -f printf-width4.awk items.txt

$ cat report.txt
Num	Description	Type      	Price	Qty
-----------------------------------------------------
103	MP3 Player	Audio     	$270.00	015
104	Tennis Racket	Sports    	$190.00	020

另一种方法是在打印语句中不指定 ">report.txt"">>report.txt"。 相反,在执行 awk 脚本时,将输出重定向到 report.txt,如下所示。

$ cat printf-width5.awk
BEGIN {
  FS=","
  printf "%-3s\t%-10s\t%-10s\t%-5s\t%-3s\n","Num","Description","Type","Price","Qty"
  printf "-----------------------------------------------------\n"
}
{
 if ($5 > 10)
   printf "%-3d\t%-10s\t%-10s\t$%-.2f\t%03d\n",$1,$2,$3,$4,$5
}

$ awk -f printf-width5.awk items.txt > report.txt

$ cat report.txt
Num	Description	Type      	Price	Qty
-----------------------------------------------------
103	MP3 Player	Audio     	$270.00	015
104	Tennis Racket	Sports    	$190.00	020