How to treat files as huge string with macos grep?

Viewed 243

I'm using the following command on Ubuntu to list all files containing a given pattern:

for f in *; do if grep -zoPq "foo\nbar" $f; then echo $f; fi; done

But on macos, I'm geting the following error:

grep: invalid option -- z

There's no -z option to treat files as a big string with macos grep, unlike gnu grep.

Is there another option on macos grep equivalent to `-z ? If not, what alternative can I use to get the same result ?

4 Answers

-P (PERL regex) is only supported in gnu grep but not on BSD grep found on Mac OS.

You can either use home brew to install gnu grep or else use this equivalent awk command:

awk 'p ~ /foo$/ && /^bar/ {print FILENAME; nextfile}; {p=$0}' *

Please note that this eliminates the need to use shell for loop.

You can install pcregrep via home brew, and then use it with the -M option:

By default, each line that matches a pattern is copied to the standard output, and if there is more than one file, the file name is output at the start of each line, followed by a colon. However, there are options that can change how pcregrep behaves. In particular, the -M option makes it possible to search for patterns that span line boundaries. What defines a line boundary is controlled by the -N (--newline) option.

With ripgrep

rg -lU 'foo\nbar'

This will list all filenames containing foo\nbar in the current directory. -U option allows to match multiple lines. Unlike grep -z, whole file isn't read in one-shot, so this is safe to use even for larger input files.

ripgrep recursively searches by default. Use rg -lU --max-depth 1 'foo\nbar' if you don't want to search sub-directories.

However, note that by default, rigprep ignores

  • files and directories that match rules specified by ignore files like .gitignore
  • hidden files and directories
  • binary files

You can change that by using:

  • -u or --no-ignore
  • -uu or --no-ignore --hidden
  • -uuu or --no-ignore --hidden --binary

It seems you are searching for files which have the sequence foo\nbar. With GNU awk (brew install gawk), you can set the record separatorRS to this sequence and check if the record matches:

gawk 'BEGIN{RS="foo\nbar"}{exit (RT!=RS)}' file

This will try to split your files in records which are separated by the record separator RS, if so, it will terminate with exit code 0, otherwise with exit code 1. The behaviour is the same as the proposed grep

If you just want the files listed, you can do:

gawk 'BEGIN{RS="foo\nbar"}(RT==RS){print FILENAME}{nextfile}' * 
Related