使用 sed 干净地交换两个字符串的所有出现

Set*_*eth 15 sed

假设我有一个文件,其中包含多次出现的 StringA 和 StringB。我想用 StringB 替换所有出现的 StringA,并且(同时)用 StringA 替换所有出现的 StringB。

现在,我正在做类似的事情

cat file.txt | sed 's/StringB/StringC/g' | sed 's/StringA/StringB/g' | sed 's/StringC/StringA/g'
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于它假定文件中没有出现 StringC。虽然这在实践中不是问题,但这个解决方案仍然让人感觉很脏——也就是说,它感觉像是一个学习更多 Unix 魔法的机会。:)

Gil*_*il' 15

如果StringBStringA不能出现在同一输入行上,那么您可以告诉 sed 以一种方式执行替换,并且只有在没有出现第一个搜索字符串时才尝试另一种方式。

<file.txt sed -e 's/StringA/StringB/g' -e t -e 's/StringB/StringA/g'
Run Code Online (Sandbox Code Playgroud)

在一般情况下,我认为 sed 中没有简单的方法。顺便说一句,请注意,如果StringA并且StringB可以重叠,则规范是不明确的。这是一个 Perl 解决方案,它替换任一字符串的最左边出现并重复。

<file.txt perl -pe 'BEGIN {%r = ("StringA" => "StringB", "StringB" => "StringA")}
                    s/(StringA|StringB)/$r{$1}/ge'
Run Code Online (Sandbox Code Playgroud)

如果您想坚持使用 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
}'
Run Code Online (Sandbox Code Playgroud)


don*_*sti 9

现在,我正在做类似的事情
…………
这种方法的问题在于它假设文件中没有出现 StringC。

我认为你的方法很好,你应该只使用其他东西而不是字符串,不能在一行中出现的东西(在模式空间中)。最佳人选是\newline。
通常,模式空间中的任何输入行都不会包含该字符,因此,要交换文件中所有出现的THISTHAT,您可以运行:

sed 's/THIS/\
/g
s/THAT/THIS/g
s/\n/THAT/g' infile
Run Code Online (Sandbox Code Playgroud)

或者,如果您的 sed 也支持\nRHS:

sed 's/THIS/\n/g;s/THAT/THIS/g;s/\n/THAT/g' infile
Run Code Online (Sandbox Code Playgroud)