第N个字符串匹配然后删除后面的字符串并用sed放一个新字符串

时间:2015-04-17 19:11:12

标签: string sed

我有这个文件:

  "Max": 10,
  "Enable": true,
  "Min": 2,
  "Enable": true,
  "Retries": 5,

我想通过删除“启用”之后的所有内容来更改第二次出现的启用值:并输入我需要的值。 即使我用相同的字符串替换,我也不在乎“启用”只是替换现在的值。

我使用了这个命令:

sed -re 's/("Enable":)[^:]/\1 false,/2' file > newfile

但结果我得到了:

  "Max": 10,
  "Enable": true,
  "Min": 2,
  "Enable": false,true,
  "Retries": 5,

如果可能,我需要使用sed命令。

谢谢大家!

3 个答案:

答案 0 :(得分:2)

我知道你说你想要sed但sed是单行上的简单替换,这不是,所以你应该使用awk:

$ awk '/Enable/ && (++c==2) {$2="false,"} 1' file
"Max": 10,
"Enable": true,
"Min": 2,
"Enable": false,
"Retries": 5,

答案 1 :(得分:2)

这在sed中比在awk中更为迂回,但没关系。你所拥有的想法并不错,但是你遇到了sed以基于行的方式工作的问题:sed在模式空间中读取一行,在其上运行代码,然后读取下一行。除非进行特殊处理,否则您的代码一次只能看到一行,因此它不会看到第二次出现"Enable":是第二次出现。

如果文件足够小以便完全保存在RAM中,那么简单的方法就是在处理之前将文件完全读入RAM:

sed ':a $! { N; ba }; s/\("Enable":\)[^\n]*/\1 false,/2' filename

那是:

:a                                  # jump label for looping
$! {                                # Unless the end of input was reached
  N                                 # fetch the next line, append it
  ba                                # go to :a
}                                   # this reads the whole file into the
                                    # pattern space. Then:
s/\("Enable":\)[^\n]*/\1 false,/2   # replace second occurrence of a matching
                                    # value.

如果您正在使用Mac OS X或BSD:那些带有BSD sed的,这需要一些......啊...说服使用跳转指令。我相信它应该接受

sed -e ':a' -e '$! { N; ba' -e '}; s/\("Enable":\)[^\n]*/\1 false,/2' filename

可替换地,

sed '1h; 1!H; $!d; x; s/\("Enable":\)[^\n]*/\1 false,/2' filename

几乎完全相同,除了它在保持缓冲区而不是模式空间中汇编文件。

如果文件非常大,您可以使用

sed '1 { x; s/^/../; x; }; /"Enable":/ { x; /^.$/ { x; s/:.*/: false,/; x; }; s/.//; x; }' filename

其工作原理如下:

1 {                  # in the first line
   x                 # set a counter in the hold buffer
   s/^/../           # two dots here to replace the second occurrence.
   x                 # (use n dots to replace the nth)
}
/"Enable":/ {        # in a matching line:
  x                  
  /^.$/ {            # check the counter. If it is one,
    x
    s/:.*/: false,/  # replace value
    x
  }
  s/.//              # decrease counter
  x
}

x交换保持缓冲区和模式空间,以便保持缓冲区的内容位于模式空间中并可以使用。他们中的很多人都认为合适的东西在合适的时间处于模式空间。

答案 2 :(得分:0)

<强>(更新) 本答案中提出的解决方案是将完整的文档读入内存......

使用Perl:在两个&#34;启用&#34;后,将.*替换为false

perl -p0E 's!"Enable" (.|\n)*? "Enable": \K .* ! false!x' 

在(gnu)Sed中,使用-z(由NULL char分隔的寄存器)文件将成为 一个寄存器。在这种情况下,可以使用解决方案(类似于OP暂定):

sed -zr 's!"Enable":[^\n]*!"Enable": false!2'
or
sed -zr 's!("Enable":)[^\n]*!\1 false!2'

使用Perl策略在sed中无效,因为我们没有非贪婪的正则表达式运算符(如.*?)。所以

sed -zr 's!("Enable"(.|\n)*?"Enable":)[^\n]*!\1 false!' ### WRONG 

错了:只要我们有2次出现&#34;启用&#34;

就行了