我正在尝试获取模式 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
简单的逻辑:
- 在第一个 *_START 标记处,启用 p=1。这将丢弃第一个 *_START 标记之前的那些行。
- 对于每一行,将当前行追加到局部变量。
- 在每个 *_END 标记处,打印局部变量并将其清空。
- 由于我们只在 *_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