如何获取模式第一次出现和最后一次出现之间的所有行?

如何获取模式第一次出现和最后一次出现之间的所有行?

如何修剪文件(井输入流),以便我只获得从第一次出现的模式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'

相关内容