我了解如何使用 awk 的 printf 函数,但我不想指定每个字段。
例如,假设这是我的文件:
c1|c2|c3|c4|c5
c6|c7|c8|c9|c10
c11|c12|c13|c14|c15
我想对其进行格式化,以便每个记录的第一个字段的宽度为 c11——第一个字段中最长的单元格:
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15
我知道我可以指定:
awk -F"|" '{printf "%-3s%s%s%s%s\n", $1, $2, $3, $4, $5}' file > newfile
假设我知道第一列的宽度是多少,但我不知道文件中有多少个字段。基本上我想做一些类似的事情:
... '{printf "%-3s|", $1}'
...然后以其原始格式打印其余字段。
答案1
您只能使用sprintf
重新格式化$1
。
前任。
$ awk 'BEGIN{OFS=FS="|"} {$1 = sprintf("%-3s",$1)} 1' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15
答案2
要计算出第一个字段的最大/最长长度,然后根据该长度重新格式化该字段中的值,您必须对文件进行两次单独的传递。
awk 'BEGIN { OFS = FS = "|" }
FNR == NR { if (m < (n=length($1))) m = n; next }
{ $1 = sprintf("%-*s", m, $1); print }' file file
(注意输入文件在命令行中指定了两次)
对于您提供的数据,这会产生
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15
第一遍由FNR == NR
块处理,它只是跟踪迄今为止看到的最长的字段(m
包含看到的最大长度),然后跳到下一行。
第二遍由最后一个块处理,该块使用 重新格式化第一个字段sprintf()
。格式字符串的%-*s
意思是“左对齐的字符串,其宽度由保存实际字符串的参数之前的整数参数给出”。
m
显然,可以通过将标量转换为保存每列最大宽度的数组来扩展到所有列:
$ awk 'BEGIN { OFS = FS = "|" }
FNR == NR { for (i=1; i<=NF; ++i) if (m[i] < (n=length($i))) m[i] = n; next }
{ for (i=1; i<=NF; ++i) $i = sprintf("%-*s", m[i], $i); print }' file file
c1 |c2 |c3 |c4 |c5
c6 |c7 |c8 |c9 |c10
c11|c12|c13|c14|c15
答案3
智能的方法是Steeldriver 建议什么。不必要的复杂方法是迭代每个字段:
$ awk -F'|' '{printf "%-3s|",$1; for(i=2;i<NF;i++){printf "%s|",$i} printf "%s\n", $i}' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15
但就sprintf
$1
这样吧。
答案4
在 Awk 中,您可以使用“*”来生成动态 printf 格式字符串。
如果您已经知道长度,则可以使用 -v 传递第一列的字段长度。
awk -vcol1=3 'BEGIN{FS="|"}{for(i=1;i<=NF;i++){if(i==1)printf "%*-s%s",col1,$i,FS;else if(i!=NF)printf "%s%s",$i,FS;else printf "%s\n",$i;};}' test.txt
注意:如果您不知道第一列的长度是多少,您可以将这些值存储在数组中,然后找到最大列长度并在 END 块中将其全部打印出来。