我正在使用awk
(或gawk
) 转换一些数据,并希望在再次打印输出之前删除其中一个输入字段。
我想要实现的是:
~ $ echo 'field1,field2,field3' | awk -F, '{transform($1); delete($2); print $0;}'
new_field1,field3
我不能只分配一个空字符串,$2
因为这会导致new_field1,,field3
(注意两个逗号)。
我可以明确地只打印我想要的字段,但这不是很优雅,因为我的字段远多于 3 个,而且末尾还有可选字段(此处未显示)。这就是为什么我更喜欢print $0
.只需要先删除一些字段即可。
任何想法?
答案1
在 awk 中删除字段是出了名的困难。这似乎是一个简单的(而且经常是必需的)操作,但它比应有的更难。
看有没有办法完全删除 awk 中的字段,以便不打印多余的分隔符? 来自 Stack Overflow 的一个好方法。
我已经复制了rmcol()
@ghoti 的答案中的函数,以便我们在 U&L 上有一个副本:
function rmcol(col, i) {
for (i=col; i<NF; i++) {
$i=$(i+1)
}
NF--
}
它从当前输入行中删除指定的列,并减少字段计数器 ( NF
) 以进行匹配。
我不知道你的transform()
函数是做什么的,所以我什至不会尝试重复它 - 但这里有一个在单行rmcol()
中使用的示例:awk
$ echo 'field1,field2,field3' | awk -F, -v OFS=, '
function rmcol(col, i) {
for (i=col; i<NF; i++) {
$i=$(i+1)
}
NF--
}
{ rmcol(2); print; }
'
field1,field3
顺便说一句,如果您需要从输入行中删除多个字段,最好/最简单的方法是按相反的顺序删除它们。那是,首先删除编号最大的字段。为什么?因为每次删除较低编号的字段时,较高编号的字段都会重新编号,这使得很难跟踪哪个字段编号属于哪个字段。
顺便说一句,delete()
inawk
用于删除数组的元素 - 而不是用于从输入行删除字段。您可以将split()
每个输入行( on FS
)放入一个数组中并删除第二个数组元素,但随后您必须编写一个join()
函数来打印数组,并用逗号(或OFS
)分隔每个字段。
即使这样做也会比预期的更复杂,因为所有数组awk
都是关联数组(即它们是不是数字索引) - 所以delete(array[2])
惯于自动将数组元素 3+ 移动到元素 2+ 中。您必须编写自己的包装函数,才能对数组执行与输入字段delete()
几乎相同的操作。rmcol()
答案2
一些替代方案
1)预处理输入以首先删除字段,cut
如果字段分隔符是单个字符,则很容易做到
$ s='field1,field2,field3'
$ # use 'cut -d, -f1,3-' if --complement option is not available
$ echo "$s" | cut -d, --complement -f2
field1,field3
$ echo "$s" | cut -d, --complement -f2 | awk 'BEGIN{FS=OFS=","} {$1="new"} 1'
new,field3
2)使用perl
$ # indexing starts from 0, the array @F contains the input fields
$ # $#F will give index of last element in the array
$ echo "$s" | perl -F, -lane '$F[0]="new"; print join ",", @F[0,2..$#F]'
new,field3
答案3
用 重写 $0gensub
可能会导致更简单的脚本。
要从给定的 input 中删除字段 2 和 3,您可以使用从 $0 中删除字段gensub
并重新生成 $0 (以及所有字段),如下所示:
> echo 'field1,field2,field3' \
| awk -F, '{OFS=","; \
transform($1); \
$0=gensub(/[^,]*,/,"",2); \
print}'
new_field1,field3
请注意,这print
相当于print $0
.