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 bl
awk one-liner:
awk '/abc/,/efg/' [file-with-content]
As an alternative to Balu Mohan's answer, it is possible to enforce the order of the patterns using only grep
, head
and tail
:
for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done
This one isn't very pretty, though. Formatted more readably:
for f in FILEGLOB; do
tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \
| grep -q "pattern2" \
&& echo $f
done
This will print the names of all files where "pattern2"
appears after "pattern1"
, or where both appear on the same line:
$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt
tail -n +i
- print all lines after the i
th, inclusivegrep -n
- prepend matching lines with their line numbershead -n1
- print only the first rowcut -d : -f 1
- print the first cut column using :
as the delimiter2>/dev/null
- silence tail
error output that occurs if the $()
expression returns emptygrep -q
- silence grep
and return immediately if a match is found, since we are only interested in the exit codeI'm not sure if it is possible with grep, but sed makes it very easy:
sed -e '/abc/,/efg/!d' [file-with-content]
This can be done easily by first using tr
to replace the newlines with some other character:
tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'
Here, I am using the alarm character, \a
(ASCII 7) in place of a newline.
This is almost never found in your text, and grep
can match it with a .
, or match it specifically with \a
.
I released a grep alternative a few days ago that does support this directly, either via multiline matching or using conditions - hopefully it is useful for some people searching here. This is what the commands for the example would look like:
Multiline:
sift -lm 'abc.*efg' testfile
Conditions:
sift -l 'abc' testfile --followed-by 'efg'
You could also specify that 'efg' has to follow 'abc' within a certain number of lines:
sift -l 'abc' testfile --followed-within 5:'efg'
You can find more information on sift-tool.org.
This should work too?!
perl -lpne 'print $ARGV if /abc.*?efg/s' file_list
$ARGV
contains the name of the current file when reading from file_list
/s
modifier searches across newline.