How to find patterns across multiple lines using grep?

后端 未结 26 1506
你的背包
你的背包 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:15

    awk one-liner:

    awk '/abc/,/efg/' [file-with-content]
    
    0 讨论(0)
  • 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
    0 讨论(0)
  • 2020-11-22 04:18

    I'm not sure if it is possible with grep, but sed makes it very easy:

    sed -e '/abc/,/efg/!d' [file-with-content]
    
    0 讨论(0)
  • 2020-11-22 04:18

    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.

    0 讨论(0)
  • 2020-11-22 04:19

    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.

    0 讨论(0)
  • 2020-11-22 04:19

    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.

    0 讨论(0)
提交回复
热议问题