首页 文章

如何用sed一次替换多个模式?

提问于
浏览
140

假设我有'abbc'字符串,我想替换:

  • ab - > bc

  • bc - > ab

如果我尝试两次替换,结果不是我想要的:

echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g'
abab

那么我可以使用什么sed命令来替换如下?

echo abbc | sed SED_COMMAND
bcab

EDIT :实际上文本可能有两个以上的模式,我不知道需要多少替换 . 因为有一个答案说 sed 是一个流编辑器,并且它的替换是贪婪的,我认为我需要使用一些脚本语言 .

7 回答

  • 9

    也许是这样的:

    sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'
    

    ~ 替换为您知道不在字符串中的字符 .

  • 5

    以下是ooga's answer的变体,适用于多个搜索和替换对,而无需检查如何重用值:

    sed -i '
    s/\bAB\b/________BC________/g
    s/\bBC\b/________CD________/g
    s/________//g
    ' path_to_your_files/*.txt
    

    这是一个例子:

    之前:

    some text AB some more text "BC" and more text.
    

    后:

    some text BC some more text "CD" and more text.
    

    注意 \b 表示字边界,这是阻止 ________ 干扰搜索的原因(我在Ubuntu上使用GNU sed 4.2.2) . 如果您不使用单词边界搜索,则此技术可能无效 .

    另请注意,这与删除 s/________//g 并将 && sed -i 's/________//g' path_to_your_files/*.txt 附加到命令末尾的结果相同,但不需要指定路径两次 .

    如果您知道文件中没有空值,则使用 \x0_\x0_ 代替 ________ ,这是一个常见的变体,as jthill suggested .

  • 0

    我总是使用带有“-e”的多个语句

    $ sed -e 's:AND:\n&:g' -e 's:GROUP BY:\n&:g' -e 's:UNION:\n&:g' -e 's:FROM:\n&:g' file > readable.sql

    这将在所有AND,GROUP BY,UNION和FROM之前附加'\ n',而'&'表示匹配的字符串,'\ n&'表示您希望在'匹配之前用'\ n'替换匹配的字符串“

  • 223

    sed 是一个流编辑器 . 它贪婪地搜索和替换 . 做你要求的唯一方法是使用中间替换模式并最终将其更改回来 .

    echo 'abcd' | sed -e 's/ab/xy/;s/cd/ab/;s/xy/cd/'

  • 4

    这可能适合你(GNU sed):

    sed -r '1{x;s/^/:abbc:bcab/;x};G;s/^/\n/;:a;/\n\n/{P;d};s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/;ta;s/\n(.)/\1\n/;ta' file
    

    这使用查找表,该查找表准备并保持在保持空间(HS)中,然后附加到每一行 . 一个独特的标记(在这种情况下为 \n )被添加到行的开头,并用作在整个行长度上沿着搜索进行碰撞的方法 . 一旦标记到达该行的末尾,该过程就完成并打印出查找表并丢弃标记 .

    注:查找表在最开始时准备好,并选择第二个唯一标记(在本例中为 : ),以便不与替换字符串冲突 .

    有一些评论:

    sed -r '
      # initialize hold with :abbc:bcab
      1 {
        x
        s/^/:abbc:bcab/
        x
      }
    
      G        # append hold to patt (after a \n)
    
      s/^/\n/  # prepend a \n
    
      :a
    
      /\n\n/ {
        P      # print patt up to first \n
        d      # delete patt & start next cycle
      }
    
      s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/
      ta       # goto a if sub occurred
    
      s/\n(.)/\1\n/  # move one char past the first \n
      ta       # goto a if sub occurred
    '
    

    该表的工作方式如下:

    **   **   replacement
    :abbc:bcab
     **   **     pattern
    
  • 4

    Tcl有一个builtin

    $ tclsh
    % string map {ab bc bc ab} abbc
    bcab
    

    这可以通过在字符串中一行一个字符来进行字符串比较,从当前位置开始 .

    在perl中:

    perl -E '
        sub string_map {
            my ($str, %map) = @_;
            my $i = 0;
            while ($i < length $str) {
              KEYS:
                for my $key (keys %map) {
                    if (substr($str, $i, length $key) eq $key) {
                        substr($str, $i, length $key) = $map{$key};
                        $i += length($map{$key}) - 1;
                        last KEYS;
                    }
                }
                $i++;
            }
            return $str;
        }
        say string_map("abbc", "ab"=>"bc", "bc"=>"ab");
    '
    
    bcab
    
  • 2

    这是 awk 基于oogas sed

    echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
    bcab
    

相关问题