How to find patterns across multiple lines using g

2018-12-31 09:42发布

I want to find files that have "abc" AND "efg" in that order, and those two strings are on different lines in that file. Eg: a file with content:

blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..

Should be matched.

标签: regex grep
21条回答
心情的温度
2楼-- · 2018-12-31 10:00

You can do that very easily if you can use Perl.

perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt

You can do that with a single regular expression too, but that involves taking the entire contents of the file into a single string, which might end up taking up too much memory with large files. For completeness, here is that method:

perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt
查看更多
呛了眼睛熬了心
3楼-- · 2018-12-31 10:04

While the sed option is the simplest and easiest, LJ's one-liner is sadly not the most portable. Those stuck with a version of the C Shell will need to escape their bangs:

sed -e '/abc/,/efg/\!d' [file]

This unfortunately does not work in bash et al.

查看更多
孤独寂梦人
4楼-- · 2018-12-31 10:04

The filepattern *.sh is important to prevent directories to be inspected. Of course some test could prevent that too.

for f in *.sh
do
  a=$( grep -n -m1 abc $f )
  test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue 
  (( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done

The

grep -n -m1 abc $f 

searches maximum 1 matching and returns (-n) the linenumber. If a match was found (test -n ...) find the last match of efg (find all and take the last with tail -n 1).

z=$( grep -n efg $f | tail -n 1)

else continue.

Since the result is something like 18:foofile.sh String alf="abc"; we need to cut away from ":" till end of line.

((${z/:*/}-${a/:*/}))

Should return a positive result if the last match of the 2nd expression is past the first match of the first.

Then we report the filename echo $f.

查看更多
人气声优
5楼-- · 2018-12-31 10:07

If you need both words are close each other, for example no more than 3 lines, you can do this:

find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

Same example but filtering only *.txt files:

find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

And also you can replace grep command with egrep command if you want also find with regular expressions.

查看更多
深知你不懂我心
6楼-- · 2018-12-31 10:08

With silver searcher:

ag 'abc.*(\n|.)*efg'

similar to ring bearer's answer, but with ag instead. Speed advantages of silver searcher could possibly shine here.

查看更多
无色无味的生活
7楼-- · 2018-12-31 10:10

you can use grep incase you are not keen in the sequence of the pattern.

grep -l "pattern1" filepattern*.* | xargs grep "pattern2"

example

grep -l "vector" *.cpp | xargs grep "map"

grep -l will find all the files which matches the first pattern, and xargs will grep for the second pattern. Hope this helps.

查看更多
登录 后发表回答