bash-delete如果两个模式之间存在字符串

时间:2015-01-02 19:13:22

标签: regex bash sed

我一直在尝试使用sed来完成以下操作。假设我有以下文件(注意:我的实际文件比这更复杂):

hello world
foo bar
people people
target
something
done

我想检查两个模式之间是否存在target,在此示例中,行foo bardone之间是否存在(包括两行),如果{{{},则删除整个模式1}}确实存在。

我知道如何使用此target命令删除两个模式之间的行:

sed

但是如果字符串sed '/people.*/,/done/d' file 存在于两个字符串匹配之间,我只想删除它。

我的逻辑是这样的:

target

修改

我忘了提及在同一行的sed -n '/people.*/,/done/p' file | check if target string exists | delete entire pattern found by sed 之前和target之后可以有任意数量的字词。

4 个答案:

答案 0 :(得分:4)

<强>桑达

如果发现$start,则会从$end移至$pattern

sed ":a;N;\$!ba; s/$start.*$pattern.*$end//g"

这里有两个步骤(陈述):

  1. 将整个文件作为单个字符串读取(根据文件大小可能不好)。有关非常好的解释,请参阅https://stackoverflow.com/a/1252191。唯一的区别是$!ba之前的额外反引号,使其适用于双引号,这对于在sed行中传递Bash变量很有用。
  2. 常规旧搜索/替换。
  3. <强>的Perl

    要处理不合适的匹配,如果允许Perl,请使用:

    perl -0777 -p -e 's/$start.*?$pattern.*?$end//s'

    这也将整个文件作为字符串读取。最后的/ s告诉它包含换行符作为正则表达式匹配的一部分。使用。*而不是。*?回到贪婪的搜索。

答案 1 :(得分:2)

sed是单行上简单替换的优秀工具,但是在20世纪70年代中期发明awk时所有用于处理多行的构造都变得过时,因此只需使用awk以简化,清晰,稳健等。使用GNU awk进行多字符RS:

$ awk -v RS='^$' '{sub(/\nfoo bar\n.*target.*\ndone\n/,""); print}' file
hello world

答案 2 :(得分:1)

如果文件首次包含done,则不首先将整个文件读入内存并邀请贪婪匹配问题的方法是

sed '/^people/ { :loop; N; /\ndone/ ! b loop; /target/ d }' filename

在Mac OS X上,显然需要在结束括号之前设置换行符,因此您可以将代码放入多行字符串文字中:

sed '/^people/ { :loop; N; /\ndone/ ! b loop; /target/ d 
}' filename

或者将此代码(无论如何更具可读性)放在文件中,例如foo.sed,然后使用sed -f foo.sed filename

/^people/ {
  :loop
  N
  /\ndone/ ! b loop
  /target/ d
}

代码的工作原理如下:

/^people/ {

以&#34;人员&#34;

开头
  :loop
  N
  /\ndone/ ! b loop

在循环中获取更多行,直到以done开头(这将是第一次\ndone出现在模式空间中)

  /target/ d

如果有target的所有内容,请丢弃整件事

}

否则照常进行(这意味着打印图案空间,因为我们没有将-n传递给sed)。

稳健性的一个可能的改进是

sed '/^people/ { :loop; N; /\ndone$/! { $! b loop }; /target/ d }' filename

/^people/ {
  :loop
  N
  /\ndone/ ! {
    $ ! b loop
  }
  /target/ d
}

更改/\ndone$/! { $! b loop }。这将结束文件最后一行的循环,即使没有遇到done,这会导致文件末尾的未完成people部分不被丢弃(除非它们包含{{ 1}})。

答案 3 :(得分:1)

迟到的回答

sed '/^foo bar *$/,/^done *$/{/^done *$/!{H;d};/^done *$/{H;g;s/.*//g;x;/.*target.*/d;s/^.//g}}'

查找/^foo bar *$/,/^done *$/

之间的所有行
/foo bar/,/done/

/^done *$/!{H;d}从foo栏获取所有行,但不是最后一行“完成”并将其放入保留空间。然后从模式空间中删除这些行。

/^done *$/{H;g;s/.*//g;x;将最后一行“完成”并将其附加到保留空间。现在我们拥有从行foo bar到保留空间中完成的行的所有行。之后,我们清除模式空间中的所有内容,并将保留空间中的行范围与模式空间中的空行交换(这是为了在目标另一行之间保持空格时保持空白“ foo bar“和”完成“。

最后

/.*target.*/d 

我们测试“目标”是否在mutli-pattern空间中。如果是,则“foo bar”和“done”之间的行范围将被删除

这样可以避免将整个文件作为单个字符串读取

示例

hello world
foo bar
people people
target
something
done
foo bar
.....
.....
.....
done
foo bar
people people
test
something
done

<强>结果

hello world
foo bar
.....
.....
.....
done
foo bar
people people
test
something
done

注意:正在删除从“foo bar”开始到包含“target”的行“done”的行的范围