How to find patterns across multiple lines using grep?

后端 未结 26 2231
你的背包
你的背包 2020-11-22 04:14

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         


        
26条回答
  •  感动是毒
    2020-11-22 04:17

    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
    

    Explanation

    • tail -n +i - print all lines after the ith, inclusive
    • grep -n - prepend matching lines with their line numbers
    • head -n1 - print only the first row
    • cut -d : -f 1 - print the first cut column using : as the delimiter
    • 2>/dev/null - silence tail error output that occurs if the $() expression returns empty
    • grep -q - silence grep and return immediately if a match is found, since we are only interested in the exit code

提交回复
热议问题