我正在尝试获取casestartStr
和之间的所有内容。我了解如何使用和之间出现的所有事件。我不知道如何将其限制为仅发生的一个实例。endStr
bbb
startStr
endStr
sed
bbb
输入示例:
fff
startStr
aaa
bbb
ccc
endStr
xxx
yyy
startStr
ddd
endStr
ddd
bbb
所需输出:
startStr
aaa
bbb
ccc
endStr
这就是我所拥有的:
$ sed -n -e '/startStr/,/endStr/ p' sample.txt
startStr
aaa
bbb
ccc
endStr
startStr
ddd
endStr
答案1
对于第一个startStr
… endStr
,包含/bbb/
出现:
sed -n '/startStr/ {:n; N; /endStr/ {/\n[^\n]*bbb[^\n]*\n/ {p; q}; b}; bn}'
或者
sed -n '/startStr/ {:n; N; /endStr/ {/\nbbb\n/ {p; q}; b}; bn}'
ifbbb
不是正则表达式,它正是您需要的字符串(从 begin 到\n
)。
解释
对于地址/startStr/
,我们:
- 设置标签
:n
; - 阅读下一行
N
; - 检查它是否匹配
/endStr/
;- 如果这是真的,检查
/\nbbb\n/
我们读到的这个块中的出现情况;- 如果存在,则执行
{p; q}
“打印并退出”, - 否则,执行
b
“抛出此块并开始搜索下一个”;
- 如果存在,则执行
- 如果这是真的,检查
- 如果不是块结束,我们跳转到
:n
,即继续阅读。
答案2
我pcregrep
对这份工作的建议是:
pcregrep -M 'startStr(.|\n)*?bbb(.|\n)*?endStr' sample.txt
选项-M
允许匹配多行模式,并且*?
in 无贪婪运算符。其余的应该是显而易见的。
答案3
修改输入样本以包含之前startStr...endStr
没有bbb
匹配块的块
$ cat ip.txt
startStr
foo
bar
endStr
fff
baz
startStr
aaa
bbb
ccc
endStr
xxx
yyy
startStr
ddd
endStr
ddd
bbb
awk
解决方案
awk '/startStr/{f=1; m=0; buf = $0; next}
/bbb/ && f{m=1}
f{buf = buf ORS $0}
/endStr/ && f{f=0; if(m==1)print buf}
' ip.txt
/startStr/{f=1; m=0; buf = $0; next}
设置标志来指示块的开始,清除匹配,初始化缓冲区并移至下一行/bbb/ && f{m=1}
如果行包含bbb
,则设置匹配。f
用于避免匹配bbb
外部startStr...endStr
f{buf = buf ORS $0}
只要设置了标志,就累积输入行/endStr/ && f{f=0; if(m==1)print buf}
在块末尾,如果找到匹配则打印缓冲区
作为一行:
$ awk '/startStr/{f=1; m=0; buf = $0; next} /bbb/ && f{m=1} f{buf = buf ORS $0} /endStr/ && f{f=0; if(m==1)print buf}' ip.txt
startStr
aaa
bbb
ccc
endStr
通过吸收整个输入文件的更简单的perl
解决方案 - 假设没有类似的块startStr...startStr...endStr
(即第一个 startStr 没有 endStr)
$ perl -0777 -ne '(@m) = /startStr.*?endStr\n/gs; print grep { /bbb/ } @m' ip.txt
startStr
aaa
bbb
ccc
endStr
答案4
sed -n -e '/startStr/,/bbb/p;/bbb/,/endStr/p' /path/to/input