One-liner to print all lines between two patterns

橙三吉。 提交于 2020-01-01 16:44:08

问题


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

回答1:


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.




回答2:


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



回答3:


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'



回答4:


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.



来源:https://stackoverflow.com/questions/35933989/one-liner-to-print-all-lines-between-two-patterns

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!