【问题标题】:grep or sed to match a block between start and end using same patterngrep 或 sed 使用相同的模式匹配 start 和 end 之间的块
【发布时间】:2018-01-23 03:05:27
【问题描述】:

我有一个包含以下信息的文件:

start pattern1
line1
line2
...
end pattern1
line3
line4
start pattern2
...

我的输出应该是:
start pattern1 line1 line2 end pattern1

如果我知道pattern1 是什么,我就能做到

sed '/start pattern1/,/end pattern1/p' <file>

但在这里,我想匹配 pattern1(如 perl 正则表达式中的 \S+)并最终使用相同的(如 $1)。我该怎么做?

【问题讨论】:

  • 预期的输出是什么,您能否在您的帖子中提及。
  • 您仍然可以使用sed -n '/pattern1/,/pattern1/p' input 来满足您的要求
  • 你能举一个真实的例子吗? startend 是否存在?还是只有patern
  • 永远不要使用范围表达式,始终使用标志。这意味着您当然不能使用 sed - 请参阅 stackoverflow.com/a/17914105/1745001stackoverflow.com/q/23934486/1745001 了解如何在条件之间打印文本。
  • 澄清我的问题:'start' 和 'end' 是我想匹配的未知 pattern1 的前缀关键字。

标签: perl unix awk sed grep


【解决方案1】:

使用awk在pattern1s(含)之间打印:

$ awk '/pattern1/{p=!p;print;next} p' file
pattern1
line1
line2
...
pattern1

可以更好地定义正则表达式,例如 /^pattern1$/$0=="pattern1"

【讨论】:

    【解决方案2】:

    使用 Perl 中的 range operator,不会同时测试模式

    perl -wnE'print if /start ([A-Za-z0-9_:]+)/ ... /end $1/' intput.txt
    

    更新为实际模式,在 cmets 中指定。

    我使用捕获进行了测试(在 do 块中,而不仅仅是 print)并且它有效,但如果有其他捕获,问题可能在于等待。如果您不捕获其他正则表达式中的任何内容,则此方法有效。

    注意使用... 而不是..,以便在下一次评估之前测试正确的操作数。

    【讨论】:

    • 我在你写的时候试过这个(没有做),它似乎不起作用。它打印整个文件。
    • @user2623661 我刚刚再次尝试(在我发布时已对其进行了测试),复制粘贴单行并像您的示例一样在文件上运行它,并在之前和之后添加了行。有用。你的真实输入是什么样的?
    • @user2623661 等等……我注意到你的评论,pattern1 是“unknown”——这是什么意思?它是什么东西?它只是“开始”(或您的实际“前缀关键字”)之后的任何内容,还是有一些规则可以是什么?
    • 它是一个“单词”,意思是没有空格,并且有一些特殊字符 [A-Za-z0-9_:]
    • @user2623661 谢谢,更新,在我的测试中有效。我在我的测试文件中添加了多个这样的部分,之前、之间和之后都有其他行。
    【解决方案3】:

    也尝试使用 awk 解决方案,如果这对您有帮助,请告诉我。

    awk -v RS="" '{match($0,/start pattern1.*start pattern1/);print substr($0,RSTART,RLENGTH)}'  Input_file
    

    编辑: OP 没有显示,像 Input_file 也可能有空行,根据 CWLiu,我正在添加一个建议,它也适用于任何空行。

    awk '/start pattern1/{print;getline;while($0 !~ /start pattern1/){print;getline};print}' Input_file
    

    【讨论】:

    • 如果pattern1之间包含空行,这不起作用
    • @CWLiu:OP 没有提到这一点,这不是问题。我还添加了另一种解决方案来处理空行。
    【解决方案4】:

    所以,这是一个基于您问题的另一种解释的awk 实现(因为它不是很清楚)。

    如果您想从以start 开头的第一行中检测pattern1,然后打印每一行直到end pattern1,您可以这样做:

    $ awk '/^start / {pat=$2; next}  /^end / && $2~pat {exit}  {print}' file 
    line1
    line2
    ...
    

    【讨论】:

      【解决方案5】:

      这可能对你有用(GNU sed):

      sed -n '/pattern/,//p' file
      

      这会调用一个触发器匹配的范围,空的// 匹配最后一个正则表达式。 p 在范围开关为真时打印所有内容。

      注意-n 调用 seds 类似 grep 的性质并关闭自动打印。

      另一种选择:

      sed '/pattern/!d;:a;n;//!ba' file
      

      【讨论】:

      • +1。 // 是这里的 sed 魔法。也就是说,它会因范围内的任何替换或匹配而中断。如果需要,您必须将 sed 传递给 sed。
      • 我猜这不适用于我的情况,因为我的“模式”有一个“开始”和“结束”前缀
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-31
      • 2019-11-28
      • 1970-01-01
      相关资源
      最近更新 更多