假设我有一个文件包含多次出现的 StringA 和 StringB。我想将所有出现的 StringA 替换为 StringB,并(同时)将所有出现的 StringB 替换为 StringA。
现在,我正在做类似的事情
cat file.txt | sed 's/StringB/StringC/g' | sed 's/StringA/StringB/g' | sed 's/StringC/StringA/g'
这种方法的问题是它假设 StringC 没有出现在文件中。虽然这在实践中不是问题,但这个解决方案仍然让人感觉很脏——也就是说,它感觉像是一个学习更多 UNIX 魔法的机会。 :)
答案1
如果StringB
和StringA
不能出现在同一输入行上,那么您可以告诉 sed 以一种方式执行替换,并且只有在没有出现第一个搜索字符串时才尝试另一种方式。
<file.txt sed -e 's/StringA/StringB/g' -e t -e 's/StringB/StringA/g'
在一般情况下,我认为 sed 中没有简单的方法。顺便说一句,请注意,如果StringA
和StringB
可以重叠,则该规范是不明确的。这是一个 Perl 解决方案,它替换任一字符串的最左边出现的位置,然后重复。
<file.txt perl -pe 'BEGIN {%r = ("StringA" => "StringB", "StringB" => "StringA")}
s/(StringA|StringB)/$r{$1}/ge'
如果您想坚持使用 POSIX 工具,awk 是最佳选择。 Awk 没有用于一般参数化替换的原语,因此您需要自己开发。
<file.txt awk '{
while (match($0, /StringA|StringB/)) {
printf "%s", substr($0, 1, RSTART-1);
$0 = substr($0, RSTART);
printf "%s", /^StringA/ ? "StringB" : "StringA";
$0 = substr($0, 1+RLENGTH)
}
print
}'
答案2
现在,我正在做类似的事情
......
这种方法的问题是它假设文件中没有出现 StringC 。
我认为你的方法很好,你应该使用其他东西而不是字符串,不能出现在一行中(在模式空间中)的东西。最好的候选者是\n
eline。
通常,模式空间中的输入行不会包含该字符,因此,要交换文件中所有出现的THIS
和,您可以运行:THAT
sed 's/THIS/\
/g
s/THAT/THIS/g
s/\n/THAT/g' infile
或者,如果您的 sed\n
也支持 RHS:
sed 's/THIS/\n/g;s/THAT/THIS/g;s/\n/THAT/g' infile
答案3
我认为使用“nonce”字符串交换两个单词是完全有效的。如果你想要一个更通用的解决方案,你可以这样做:
sed 's/_/__/g; s/you/x_x/g; s/me/you/g; s/x_x/me/g; s/__/_/g' <<<"say you say me"
这产生
say me say you
x_x
请注意,如果您碰巧有“x_x”字符串,则此处需要两个额外的替换以避免替换。但即使如此,awk
对我来说似乎仍然比解决方案更简单。
答案4
我知道这是很久以前的事了,但在某些情况下你可能会使用:
echo foobar | sed -e 's/bar/foo/' -e 's/foo/bar/'
这是有效的,因为它替换了第一个出现的第一个bar
,然后替换了第一个出现的foo
,而第二个则保持foo
不变。这假设知道它们发生的顺序,并且只发生一次。
这是一个更加不可知的版本:
echo foobar | sed 's/foo/tmp/g' | sed -e 's/bar/foo/g' -e 's/tmp/bar/g'
$ echo barbarfoobar | sed 's/foo/tmp/g' | sed -e 's/bar/foo/g' -e 's/tmp/bar/g'
foofoobarfoo