除了第一次使用sed之外,如何查找和替换每个匹配?

时间:2012-10-17 14:41:53

标签: sed

我正在使用sed来查找和替换文字,例如:

set -i 's/a/b/g' ./file.txt

这会将a的每个实例替换为文件中的b。我需要添加一个例外,以便seda的每个实例替换为b,除了文件中的第一个外观,例如:

There lived a bird who liked to eat fish.
One day he fly to a tree.

这变为:

There lived a bird who liked to ebt fish.
One dby he fly to b tree.

如何修改我的sed脚本,仅将a的每个实例替换为b,第一次出现除外?

我有GNU sed版本4.2.1。

3 个答案:

答案 0 :(得分:5)

这可能适合你(GNU sed):

sed 's/a/b/2g' file

sed ':a;s/\(a[^a]*\)a/\1b/;ta' file

这可以是taylored,例如

sed ':a;s/\(\(a[^a]*\)\{5\}\)a/\1b/;ta' file
a b之后

将开始用5替换a

答案 1 :(得分:5)

您可以使用更复杂的脚本执行更完整的实现:

#!/bin/sed -nf

/a/ {
    /a.*a/ {
        h
        s/a.*/a/
        x
        s/a/\n/
        s/^[^\n]*\n//
        s/a/b/g
        H
        g
        s/\n//
    }

    : loop
    p
    n
    s/a/b/g
    $! b loop
}

使用伪代码

可以很容易地解释其功能
if line contains "a"
    if line contains two "a"s
        tmp = line
        remove everything after the first a in line
        swap tmp and line
        replace the first a with "\n"
        remove everything up to "\n"
        replace all "a"s with "b"s
        tmp = tmp + "\n" + line
        line = tmp
        remove first "\n" from line
    end-if

    loop
        print line
        read next line
        replace all "a"s with "b"s
        repeat loop if we haven't read the last line yet
    end-loop
end-if

答案 2 :(得分:1)

一种方法是替换所有然后反转第一次替换(感谢 potong ):

sed -e 'y/a/\n/' -e 's/\n/a/g' -e 'y/\n/b/'

Newline用作中间名,因此以b开头的字符串可以正常工作。

以上工作顺行,如果你想将它应用于整个文件,首先将整个文件分成一行:

<infile tr '\n' '^A' | sed 'y/a/\n/; s/\n/a/; y/\n/b/' | tr '^A' '\n'

或者更简单地使用 potong的答案中的sed命令:

<infile tr '\n' '^A' | sed 's/a/b/2g' | tr '^A' '\n'

注意^A(ASCII 0x01)可以使用 Ctrl-v Ctrl-a 生成。 ^A中的tr可以由\001替换。

这假设该文件不包含^A