0

Hi after I find the files and enclose their name with double quotes with the following command:

FILES=$(find . -type f -not -path "./.git/*" -exec echo -n '"{}" ' \; | tr '\n' ' ')

I do a for loop to grep a certain word inside each file that matches find:

for f in $FILES; do grep -Eq '(GNU)' $f; done

but grep complains about each entry that it cannot find file or directory:

grep: "./test/test.c": No such file or directory

see picture:

enter image description here

whereas echo $FILES produces:

"./.DS_Store" "./.gitignore" "./add_license.sh" "./ads.add_lcs.log" "./lcs_gplv2" "./lcs_mit" "./LICENSE" "./new test/test.js" "./README.md" "./sxs.add_lcs.log" "./test/test.c" "./test/test.h" "./test/test.js" "./test/test.m" "./test/test.py" "./test/test.pyc"

EDIT found the answer here. works perfectly!

Community
  • 1
  • 1
Sanandrea
  • 2,112
  • 1
  • 27
  • 45

1 Answers1

2

The issue is that your array contains filenames surrounded by literal " quotes.

But worse, find's -exec cmd {} \; executes cmd separately for each file which can be inefficient. As mentioned by @TomFenech in the comments, you can use -exec cmd {} + to search as many files within a single cmd invocation as possible.

A better approach for recursive search is usually to let find output filenames to search, and pipe its results to xargs in order to grep inside as many filenames together as possible. Use -print0 and -0 respectively to correctly support filenames with spaces and other separators, by splitting results by a null character instead - this way you don't need quotes, reducing possibility of bugs.

Something like this:

find . -type f -not -path './.git/*' -print0 | xargs -0 egrep '(GNU)'

However in your question you had grep -q in a loop, so I suspect you may be looking for an error status (found/not found) for each file? If so, you could use -l instead of -q to make grep list matching filenames, and then pipe/send that output to where you need the results.

find . -print0 | xargs -0 egrep -l pattern > matching_filenames

Also note that grep -E (or egrep) uses extended regular expressions, which means parentheses create a regex group. If you want to search for files containing (GNU) (with the parentheses) use grep -F or fgrep instead, which treats the pattern as a string literal.

Anders Johansson
  • 3,926
  • 19
  • 19
  • what if I do want to have a list of files that do not match the regex, **also**? – Sanandrea Mar 28 '16 at 13:55
  • The `-L` option lists files not matching. – Anders Johansson Mar 28 '16 at 13:56
  • Last question, the second option lists the (non) matching files in a file, is it possible to build an array with the result? So I do not need to read the contents of the file and afterwards delete it. – Sanandrea Mar 28 '16 at 14:00
  • Your comment about `-exec` running once per result is only true if you use `-exec cmd {} \;`. If you use `-exec cmd {} +` then it will pass multiple results to `cmd` at once. – Tom Fenech Mar 28 '16 at 14:01
  • You can just put the result in an array `()` with command substitution `$()`: `RESULT=($(find . -print0 | xargs -0 fgrep -L pattern))`. – Anders Johansson Mar 28 '16 at 14:11