3

I am trying to get the count of files which has matching keywords in directory. Code I used is:

grep -r -i --include=*.sas 'keyword' - This gives the total count of keyword.

grep -l "keyword" --include="*.sas" * | wc -l - this code is not working.

Can any one help me to, how to get the count of the files which contains the keyword.

  • 5
    Can you be more specific than "is not working"? Do you actually want a subdirectory search (as implied by the -r in your first command) or only the top level directory (as implied by the * wildcard in your second command)? – steeldriver Nov 24 '20 at 13:37
5
find $DIR  -type f  -name '*.sas'  -exec grep -m1 -e 'pattern' {} \; | wc -l

With the -m1 option, grep will output at most 1 line per file, so that way the wc -l will give you the correct number of files which contained that pattern.

  • Thanks for the response. Is there any way i can get information about the number unique .sas files in subfolder also which contain this pattern. like Folder 1 - 23 .sas files | folder2 - 56 .sas files – Code-x Go Nov 26 '20 at 8:43
  • Yes, the above command does exactly that -- gives the total count of such files in $DIR and all its subfolders. But if you want to limit the count to only the $DIR folder, then add a -maxdepth 1 before the -exec. – Pourko Nov 26 '20 at 9:20
4

I would personally use find together with grep to do this counting:

find . -type f -name '*.sas' -exec grep -q -e 'pattern' {} \; -exec echo x \; |
wc -l

Add -maxdepth 1 before the first -exec if you don't want find to recurse into subdirectories.

What this does is to run grep -q -e 'pattern' on each file with a .sas filename suffix in the current directory or below. For each file wherein pattern matches, an x will be outputted by echo x. The number of lines outputted by echo in this way is then counted by wc -l. We count xes instead of filenames with wc since filenames may contain newline characters.

For a non-recursive variant of this, you may also opt for a simple shell loop:

for name in ./*.sas; do
    [ ! -f "$name" ] && continue
    grep -q -e 'pattern' "$name" && echo x
done | wc -l

or,

n=0
for name in ./*.sas; do
    [ ! -f "$name" ] && continue
    grep -q -e 'pattern' "$name" && n=$((n + 1))
done
echo "$n"

If your pattern is not a regular expression but a string, use grep -q -F -e 'string' instead.

  • with find implementations supporting -printf option, you don't need to call echo. – pLumo Nov 25 '20 at 7:59
  • @pLumo That's true. My native find on OpenBSD does not have -printf. – Kusalananda Nov 25 '20 at 8:37
2
grep -rl --include='*.sas' keyword . | wc -l

Would work as long as file paths don't contain newline characters.

To work with those, you'd need to switch to NUL-delimited records instead of lines:

grep -rlZ --include='*.sas' keyword . | tr -cd '\0' | wc -c

(note that -r/-Z/--include are non-standard extensions and *.sas would fail to match on filenames that contain sequences of bytes not forming valid characters in the locale (like a $'st\xe9phane.sas' in a UTF-8 locale)).

2
\grep -rh -i -m1 --include='*.sas' -e 'pattern' |wc -l

-h: don't output filename. will not break then on file/path containing newline.
-m1: stop reading the rest of the file once first match found
-r: recursive
-i: case insensitive match
-e: use PATTERN as a pattern; avoid treating - as grep switch identifier if it come at the beginning of the pattern.


There is also an answer of mine on AU, that I think it's useful to take a look and use it for quick review on each switch you want to know about it instead of looking/scrolling from man grep:

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