One-liner to print all lines between two patterns

2020-07-22 16:45发布

Using one line of Perl code, what is the shortest way possible to print all the lines between two patterns not including the lines with the patterns?

If this is file.txt:

aaa
START
bbb
ccc
ddd
END
eee
fff

I want to print this:

bbb
ccc
ddd

I can get most of the way there using something like this:

perl -ne 'print if (/^START/../^END/);'

That includes the START and END lines, though.

I can get the job done like this:

perl -ne 'if (/^START/../^END/) { print unless (/^(START)|(END)/); };' file.txt

But that seems redundant.

What I'd really like to do is use lookbehind and lookahead assertions like this:

perl -ne 'print if (/^(?<=START)/../(?=END)/);' file.txt

But that doesn't work and I think I've got something just a little bit wrong in my regex.

These are just some of the variations I've tried that produce no output:

perl -ne 'print if (/^(?<=START)/../^.*$(?=END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../^.*(?=END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../(?=END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../.*(?=END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../^.*(?=END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../$(?=END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../^(?=END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../(?=^END)/);' file.txt
perl -ne 'print if (/^(?<=START)/../.*(?=END)/s);' file.txt

4条回答
贪生不怕死
2楼-- · 2020-07-22 17:00
perl -e 'print split(/.*START.|END.*/s, join("", <>))' file.txt

perl -ne 'print if /START/../END/' file.txt | perl -ne 'print unless $.==1 or eof'

perl -ne 'print if /START/../END/' file.txt | sed -e '$d' -n -e '1\!p'
查看更多
女痞
3楼-- · 2020-07-22 17:04

I don't see why you are so insistent on using lookarounds, but here are a couple of ways to do it.

perl -ne 'print if /^(?=START)/../^(?=END)/'

This finds the terminators without actually matching them. A zero-length match which satisfies the lookahead is matched.

Your lookbehind wasn't working because it was trying to find beginning of line ^ with START before it on the same line, which can obviously never match. Factor the ^ into the zero-width assertion and it will work:

perl -ne 'print if /(?<=^START)/../(?<=^END)/'

As suggested in comments by @ThisSuitIsBlackNot you can use the sequence number to omit the START and END tokens.

perl -ne '$s = /^START/../^END/; print if ($s>1 && $s !~ /E0/)'

The lookarounds don't contribute anything useful so I did not develop those examples fully. You can adapt this to one of the lookaround examples above if you care more about using lookarounds than about code maintainability and speed of execution.

查看更多
贼婆χ
4楼-- · 2020-07-22 17:10

It seems kind of arbitrary to place a single-line restriction on this, but here's one way to do it:

$ perl -wne 'last if /^END/; print if $p; $p = 1 if /^START/;' file.txt
查看更多
Fickle 薄情
5楼-- · 2020-07-22 17:22

Read the whole file, match, and print.

perl -0777 -e 'print <> =~ /START.*?\n(.*?)END.*?/gs;' file.txt

May drop .*? after START|END if alone on line. Then drop \n for a blank line between segments.


Read file, split line by START|END, print every odd of @F

perl -0777 -F"START|END" -ane 'print @F[ grep { $_ & 1 } (0..$#F) ]' file.txt

Use END { } block for extra processing. Uses }{ for END { }.

perl -ne 'push @r, $_ if (/^START/../^END/); }{ print "@r[1..$#r-1]"' file.txt

Works as it stands only for a single such segment in the file.

查看更多
登录 后发表回答