bash 在模式 1 的第一次出现和模式 2 的最后一次出现之间搜索文本



我正在尝试获取模式 1 第一次出现和模式 2 最后一次出现之间的所有行,这两种模式都是正则表达式

示例代码

TEXT
TEXT
[SUN_START]
[SUN_END]
[MON_START]
TEXT
[MON_END]
[TUE_START]
[TUE_END]
[WED_START]
TEXT
[WED_END]
TEXT
TEXT

我期待的输出是

[SUN_START]
[SUN_END]
[MON_START]
TEXT
[MON_END]
[TUE_START]
[TUE_END]
[WED_START]
TEXT
[WED_END]

模式XXX_START且XXX_END

到目前为止,我得到的是

cat /u01/app/oracle/admin/LNOPP1P/config/dbbackup_LNOPP1P.config | sed -n -e '/[[A-Z][A-Z][A-Z]_START]/,/[[A-Z][A-Z][A-Z]_END]/p'

但这不会保持换行符并像这样将所有内容一起显示

[SUN_START]
[SUN_END]
[MON_START]
TEXT
[MON_END]
[TUE_START]
[TUE_END]
[WED_START]
TEXT
[WED_END]

我还想确保它只匹配以 [[A-Z]_START] 开头的行,并且对于 END 也是如此

没有awk的解决方案,使用 grep

grep -Pzo '(?s)[([A-Z]{3})_START].*n.*[1_END]' file | sed 's/x00/nn/'

你得到,

[SUN_START][SUN_END][MON_START]发短信[MON_END][TUE_START][TUE_END][WED_START]发短信[WED_END]

*基于@albfan答案

这个awk应该有效:

awk '/_START]/{p=1} p{a = a $0 ORS}/_END]/{printf "%s", a; a="";}' file

简单的逻辑:

  1. 在第一个 *_START 标记处,启用 p=1。这将丢弃第一个 *_START 标记之前的那些行。
  2. 对于每一行,将当前行追加到局部变量。
  3. 在每个 *_END 标记处,打印局部变量并将其清空。
  4. 由于我们只在 *_END 标签处打印,因此不会打印最后一个 *_END 之后的那些行。
您可以使用

awk

awk '/[..._START]/{p=1}/[..._END]/{print;p=0}p||!NF' file

变量p是在需要打印时设置的。 !NF允许保留空行。

恕我直言,不将内容保存在内存中的两次传递方法是最简单和最健壮的:

$ awk '
    NR==FNR { if (/[[A-Z]+_START]/ && !beg) beg=NR; if (/[[A-Z]+_END]/) end=NR; next }
    FNR>=beg && FNR<=end
' file file
[SUN_START]
[SUN_END]
[MON_START]
TEXT
[MON_END]
[TUE_START]
[TUE_END]
[WED_START]
TEXT
[WED_END]

请考虑使用 [[:upper:]] 而不是 [A-Z] 来实现跨区域设置的可移植性。

我刚刚看到您在不同的答案下发表了以下评论:

Is it simple to invert this selection? select everything but the bit selected by this AWK ?

答案是"当然",只需更改脚本末尾的条件:

$ awk '
    NR==FNR { if (/[[A-Z]+_START]/ && !beg) beg=NR; if (/[[A-Z]+_END]/) end=NR; next }
    FNR<beg || FNR>end
' file file
TEXT
TEXT
TEXT
TEXT

或者保留原始条件,但将其操作设置为"下一步",并为要命中的每行添加默认的"打印":

$ awk '
    NR==FNR { if (/[[A-Z]+_START]/ && !beg) beg=NR; if (/[[A-Z]+_END]/) end=NR; next }
    FNR>=beg && FNR<=end { next }
    { print }
' file file
TEXT
TEXT
TEXT
TEXT

最新更新