Regexp标志DOTALL in sed或替代品

时间:2014-03-20 10:50:34

标签: regex bash sed

我想替换与正则表达式匹配的文件的一部分。关键是,它必须作为单个字符串(如grep -Pzo)处理整个文件,但据我所知,sed是基于行的。

我试图强制sed通过操纵IFS来强制执行此操作,但我仍然缺乏bash经验,而且我不确定我在做什么。
我希望你能帮助我澄清一些我不理解的事情。

所以我做了这样的事情:

 OIFS=$IFS
 IFS=""
 content=$(cat -v file  | sed 's/(?<=<\/div>(?!.*\/div>)).*//') 
 #Remove everything begining from last </div> to the end of file.
 IFS=$OIFS

但我没有像我预期的那样工作。我也在试验perl进行替换,但问题似乎是一样的 我会很感激任何提示。

编辑: 根据下面的评论,我正在粘贴一些示例数据:

 Source:
    <html>
    <body>
    <div>
        some site with many <div> divs </div>
           <div> and more <div> even more </div> </div>
    </div> <!-- last div closing -->
    This is all to be deleted
    </body>
    </html>

然后:s/</div>(?<=<\/div>(?!.*\/div>)).*//s

<html>
<body>
<div>
    some site with many <div> divs </div>
       <div> and more <div> even more </div> </div>


编辑2: 我找到了比下面建议更简单的方法:

cat file | perl -0pe 's/(?<=<\/div>(?!.*\/div>)).*//'

-0导致记录分隔符为空,这使得perl在一次运行中处理整个字符串而不是循环遍历行。

3 个答案:

答案 0 :(得分:3)

您可以通过撤消输入文件,删除第一个</div>之前的所有内容然后再次撤消来执行此操作:

tac input.txt | sed '1,/<\/div>/d' | tac > output.txt

这将删除包含</div>的最后一行及其后的所有内容。

替代sed(虽然不是很漂亮,但我确信有更聪明的方法):

tr '\n' '~' < input.txt | sed -r 's~(.*)</div>.*~\1~g' | tr '~' '\n' > output.txt

使用占位符(在此示例中为~)替换换行符,以便所有内容都在一行上,将该行匹配到最后一行</div>,然后再次替换换行符。根据您的输入数据选择占位符,显然它应该是不会发生的事情。

答案 1 :(得分:3)

这是一个更通用的解决方案:

$ cat file | tr '\n' '\r' | sed 's,\(.*</div>\).*,\1,' | tr '\r' '\n'
<html>
  <body>
    <div>
      some site with many <div> divs </div>
      <div> and more <div> even more </div> </div>
    </div>

说明:

tr '\n' '\r'通过回车替换换行符,因此sed会将文件内容视为一行。

sed 's,\(.*</div>\).*,\1,'删除</div>的最后一场比赛后的所有内容。

tr '\r' '\n'用换行符替换剩余的回车符。

注意:如果您的原始文件包含windows样式的\ r \ n换行符,请先转换为unix样式的换行符:

$ cat file | dos2unix | tr '\n' '\r' | sed 's,\(.*</div>\).*,\1,' | tr '\r' '\n' | unix2dos

答案 2 :(得分:0)

有些人喜欢awk

awk '/<\/div>/ {exit} 1' file

找到模式后exit