7

I read the question about how to find out the number of files that contain a certain string. This is possible with grep -l "string" * | wc -l.

Is it possible to invert this, finding the number of files not containing the target string? I tried adding the -v option, but this doesn't seem to output the right result.

10

With GNU or OpenBSD grep:

grep -L "string" ./* | grep -c /
9

Another POSIX way:

c=0
for f in *; do
  [ -d "$f" ] && continue
  { grep -q string || c=$(($c + 1)); } < "$f"
done
echo "$c"

By redirecting the command group instead of grep alone, we avoid counting as 1 the files which we cannot open (like files which we don't have read permission for, or * if there's no non-hidden file in the current directory).

With GNU grep, the equivalent would be:

grep -d skip -L foo ./* | grep -c /

Note that you can't use wc -l as filenames can be made of several lines. Having ./ also avoids problems with filenames that start with - or are - (which -- doesn't work around). Note that it ignores dot files.

4

Here's a POSIX-compliant way, in case you don't have grep -L:

for file in *; do
    awk '/string/ { found=1; exit } END{ if(!found) { printf "x" } }' < "$file"
done | wc -c
1

Another POSIX way using only grep and wc:

COUNT_FILES=0
for i in *;do COUNT_FILES=$((COUNT_FILES+1));done
MATCHES=$(grep -l "string" * | wc -l)
NON_MATCHES=$((COUNT_FILES - MATCHES))
echo "There are $NON_MATCHES files not matching \"string\""
  • Note that it will return 1 if there's no non-hidden file in the current directory. You may also want to skip directories. Another way to count the files and avoid a race condition would be set -- *, use $# and loop with for i do .... You can't use wc -l to count files as filenames can be made of several lines. – Stéphane Chazelas Jan 8 '14 at 12:47
0

Depending on how much files you want to search through, it's a good advice to take a look at ack-grep. The reason, because ack-grep is a lot faster than good'ol grep and the CLI is nearly identical.

ack-grep -Lur "some string" * | wc -l

0

With zsh:

( arr=(./*(.N^e_'grep -q PATTERN $REPLY'_)); print ${#arr}; )

This saves the names of the files that don't contain PATTERN into an array and returns the number of elements in the array. It uses glob qualifiers: . selects only regular files (add D to include hidden files), N turns on null glob and the negated (^) estring: ^e_'grep -q PATTERN $REPLY'_ further deselects the file names for which the shell code between the quotes returns true.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.