How do I grep the first 50 lines of each file in a directory recursively?

I need to search the first 50 lines of every file in a directory and its subdirectories.

This will do the recursive part, but how do I limit to just the first 50 lines of each file?

grep -r "matching string here" .

Some of these files are huge, and I only want them to match in the first 50 lines. I'm trying to speed up the process by not searching megabytes of binary data in some files.


  • If you just want the files that match:

    find . -type f -exec bash -c 'grep -q "matching string here" < <(head -n 50 "$1")' _ {} \; -printf '%p\n'
    

    or

    find . -type f -exec bash -c 'grep -q "matching string here" < <(head -n 50 "$1") && printf '%s\n' "$1"' _ {} \;
    
  • If you only want the matching strings:

    find . -type f -exec head -n 50 {} \; | grep "matching string here"
    

    or, better,

    find . -type f -exec head -q -n 50 {} + | grep "matching string here"
    
  • And if you want both:

    find . -type f -exec bash -c 'mapfile -t a < <(head -n 50 "$1" | grep "matching string here"); printf "$1: %s\n" "${a[@]}"' _ {} \;
    

Remarks.

  • Could be slightly easier with sed instead of the combo head--grep.
  • Let me stress that all three methods are 100% safe regarding file names that may contain funny symbols (spaces, newlines, etc.).
  • In two of these methods, I'm assuming you have a decently recent version of bash.
  • You could use -exec ... + in each method, but then you'll have to code your inner loop yourself! (trivial exercise left to the reader). This might be very slightly more efficient if you have a gazillion files.

If you need the grep output as in the original, you could do:

find . -type f | while read f; do 
  if head -n 50 "$f"|grep -s "matching string here"; then
    grep "matching string here" "$f" /dev/null 
  fi
done

If you only need the file names you can replace the 2nd grep with echo "$f".