运行 几个安全的字符串替换

Run several string substitutions safely

我必须 运行 在一个文本文件上进行多次替换,并且我需要区分一个字符串,该字符串是用来代替其他内容的,如果它最初存在于同一字符串中。

例如,假设我想在以下文件的第二个字段中将 a 替换为 b,并将 b 替换为 c(以获取 b c c)

a a
a b
b c

如果我运行 awk ' == "a" { = "b"}; == "b" { = "c"} 1' file显然我得到

a c
a c
b c

我可以在此处注意 运行 替换的顺序,但在实际情况中并非如此。我想要一个灵活的脚本,我可以在其中以任何顺序编写替换,而不必担心值被覆盖。我尝试过乐观 awk ' == "a" { = b}; == "b" { = c}; b = "b"; c = "c"; 1' file 但它没有用。

由于您最多只想执行一次替换,因此最好使用 if ... else if ...

awk '{
    if      ( == "a") { = "b"}
    else if ( == "b") { = "c"}
    else if ( == "c") { = "a"}
    print
}' <<END
a a
a b
b c
END
a b
a c
b a

根据您的风格格式化代码。


另一种可能更优雅的方法:

awk '
    BEGIN {repl["a"] = "b"; repl["b"] = "c"; repl["c"] = "a"}
     in repl { = repl[]}
    1
' <<END
a a
a b
b c
END

不更改刚刚更改的字符串的一般惯用方法是将旧值映射到不能出现在输入中的字符串,然后将它们转换为新值:

$ cat tst.awk
BEGIN {
    old2new["a"] = "b"
    old2new["b"] = "c"
}
{
    # Step 1 - put an "X" after every "@" so "@<anything else>"
    # cannot exist in the input from this point on.
    gsub(/@/,"@X",)

    # Step 2 - map "old"s to intermediate strings that cannot exist
    c=0
    for (old in old2new) {
        gsub(old,"@"c++,)
    }

    # Step 3 - map the intermediate strings to the new strings
    c=0
    for (old in old2new) {
        gsub("@"c++,old2new[old],)
    }

    # Step 4 - restore the "@X"s to "@"s
    gsub(/@X/,"@",)

    # Step 5 - print the record
    print
}

$ awk -f tst.awk file
a b
a c
b c

我使用 gsub()s,因为这是最常见的应用,但如果 ifs 更适合您的情况,请随意使用。

显然,仅在 @ 末尾添加连接 c++ 的方法仅适用于最多 10 个替换,您必须想出到其他字符的映射超过那(这是微不足道的,但不要被 RE 元字符绊倒)。