如何修剪文件(井输入流),以便我只获得从第一次出现的模式foo
到最后一次出现的模式的行bar
?
例如,考虑以下输入:
A line
like
foo
this
foo
bar
something
something else
foo
bar
and
the
rest
我期望这个输出:
foo
this
foo
bar
something
something else
foo
bar
答案1
sed -n '/foo/{:a;N;/^\n/s/^\n//;/bar/{p;s/.*//;};ba};'
sed 模式匹配/first/,/second/
逐行读取。当某些行匹配时,/first/
它会记住它并期待该/second/
模式的第一个匹配。同时它应用为该模式指定的所有活动。此后,该过程一次又一次开始,直到文件末尾。
那不是我们需要的。我们需要查找最后一次匹配的/second/
模式。因此,我们建造的建筑看起来只是第一个入口/foo/
。当找到时,循环a
开始。我们将新行添加到匹配缓冲区N
并检查它是否与模式匹配/bar/
。如果是,我们只需打印它并清除匹配缓冲区,然后使用 janyway 跳转到循环的开头ba
。
另外,我们需要在使用 清理缓冲区后删除换行符/^\n/s/^\n//
。我确信有更好的解决方案,不幸的是我没有想到。
希望一切都清楚。
答案2
我会用一点 Perl 语句来完成它。
cat <<EOF | perl -ne 'BEGIN { $/ = undef; } print $1 if(/(foo.*bar)/s)'
A line
like
foo
this
foo
bar
something
something else
foo
bar
and
the
rest
EOF
产量
foo
this
foo
bar
something
something else
foo
bar
答案3
这是一个不需要太多内存的两遍 GNU sed 解决方案:
< infile \
| sed -n '/foo/ { =; :a; z; N; /bar/=; ba }' \
| sed -n '1p; $p' \
| tr '\n' ' ' \
| sed 's/ /,/; s/ /p/' \
| sed -n -f - infile
解释
- 第一次
sed
调用传递 infile 并查找第一次出现的foo
和所有后续出现的bar
。 sed
然后,通过两次调用sed
和 一次将这些地址形成一个新脚本tr
。第三个的输出sed
是[start_address],[end_address]p
,不带括号。- 最后调用再次
sed
传递infile
,打印找到的地址以及其间的所有内容。
答案4
这是另一种方法sed
:
sed '/foo/,$!d;H;/bar/!d;s/.*//;x;s/\n//' infile
它将/foo/,$
范围内的每一行(!
不在此范围内的行将被d
删除)附加到H
旧空间。bar
然后删除不匹配的行。在匹配的行上,模式空间被清空,ex
随保留空间更改,并且模式空间中的前导空行被删除。
输入巨大且很少出现bar
这种情况应该比将每行拉入模式空间然后每次检查模式空间要快得多bar
。
解释:
sed '/foo/,$!d # delete line if not in this range
H # append to hold space
/bar/!d # if it doesn't match bar, delete
s/.*// # otherwise empty pattern space and
x # exchange hold buffer w. pattern space then
s/\n// # remove the leading newline
' infile
当然,如果这是一个文件(并且适合内存),您可以简单地运行:
ed -s infile<<'IN'
.t.
/foo/,?bar?p
q
IN
因为ed
能向前搜索和落后。
如果您的 shell 支持进程替换,您甚至可以将命令输出读入文本缓冲区:
printf '%s\n' .t. /foo/,?bar?p q | ed -s <(your command)
或者如果没有,则gnu ed
:
printf '%s\n' .t. /foo/,?bar?p q | ed -s '!your command'