问题描述
有人可以解释一下为什么这会按预期工作:echo "one\ntwo\nthree\n" | awk '{if (gsub(/one/,"")) { print } else {print $0}}'
two
three
echo "one\ntwo\nthree\n" | awk '{if (gsub(/four/,"")) { print } else {print $0}}'
one
two
three
但这不是吗?echo "one\ntwo\nthree\n" | awk '{if (gsub(/one/,"")) { print }}'
类似地,如果尝试链接多个替换,要求所有替换都返回非零的替换计数,然后才打印更改后的结果,否则打印原始内容:echo "one\ntwo\nthree\n" | awk '{if (gsub(/one/,"") && gsub(/two/,"")) { print } else {print $0}}'
我得到:
two
three
我期望的地方:
three
我在这里错过了什么?来自任何其他编程语言,我希望它“正常工作”。请注意,我在 BSD 和 GNU awk 中观察到相同的行为。
编辑:
我认为这与 awk 处理多行输入的方式有关:
echo "one\ntwo\nthree\n" | awk '{if (gsub(/one/,"")) print "found"; else print "not found" }'
found
not found
not found
not found
解决方法
printf 'one\ntwo\nthree\n' | awk '{if (gsub(/one/,"")) { print } else {print $0}}'
可以简化为:
printf 'one\ntwo\nthree\n' | awk '{gsub(/one/,""); print}'
因为它只是从每一行中删除 one
(如果存在)并打印每一行。
另一方面,你失败的脚本:
printf 'one\ntwo\nthree\n' | awk '{if (gsub(/one/,"")) { print }}'
可以简化为:
printf 'one\ntwo\nthree\n' | awk 'gsub(/one/,"") { print }'
从每一行中删除 one
(如果存在),但它只打印那些 gsub()
返回非零数字的行,即删除至少 1 个 one
。
您发布的其他脚本:
printf 'one\ntwo\nthree\n' |
awk '{if (gsub(/one/,"") && gsub(/two/,"")) { print } else {print $0}}'
可以简化为:
printf 'one\ntwo\nthree\n' |
awk 'gsub(/one/,""){ gsub(/two/,"") } { print }'
因此它尝试删除 one
并且如果成功则它尝试删除 twos
(因此它永远不会尝试删除 two
在同一行上没有 one
,您的输入中没有任何情况),最后不管发生了什么,它都会打印每一行。
如果您想始终同时删除 one
和 two
并打印每一行,则应该是:
printf 'one\ntwo\nthree\n' |
awk '{gsub(/one/,""); gsub(/two/,""); print }'
,
好的,有两件事:
- 我需要解析输入,以便 awk 将其视为单个记录,方法是添加
BEGIN {FS="\n"; RS=""}
- 我混淆了
print
和print $0
的用法,认为前者存储修改输入的当前值,后者存储原始值,但它们都只存储当前值。
所以我最后一个问题的解决方案是:
echo "one\ntwo\nthree\n" | awk 'BEGIN {FS="\n"; RS=""}{orig=$0;if (gsub(/one/,"")) { print } else {print orig}}'
three