6

I am trying to run the find command to find the total number of occurrences of a particular text string and also the number of files which has this text string.

What I have right now is this command.

find . -name "*.txt" | xargs grep -i "abc"

This reports all the "*.txt" files which contain the text "abc". I want either one or two find command to get

  1. Total number of times abc appears
  2. Total number of files which has abc in it.
2
  • 1
    for the sake of precision: your command (and all the other grep based solutions) do not display the number of occurrences of the text string but the number of lines where the string occurres at least once
    – miracle173
    Commented May 4, 2012 at 17:40
  • @miracle173 Good point! Commented May 4, 2012 at 18:41

4 Answers 4

6

For question 1, you can do this:

find . -name "*.txt" | xargs grep -i "abc" | wc -l

This counts the total number of matches for abc in all text files.

And for question 2, I came up with:

find . -name "*.txt" -exec grep -i "abc" {} + | cut -d: -f1 | sort | uniq | wc -l

This gets just the unique filenames from the list of matches and counts them (the sort is probably not needed).


As pointed out by miracle173, grep comes with a "one match per file" flag so the command can be shortened to just:

find . -name "*.txt" -exec grep -il "abc" {} + | wc -l

7
  • 2
    2. is a little bit complicated. grep -l will display one line for each file containing the search string.
    – miracle173
    Commented May 4, 2012 at 6:01
  • @SigueSigueBen Thanks for your answer. The first one works but for the second one looks like you are missing something after wc -. I am getting this error wc: cannot open - find: bad option out
    – Nomad
    Commented May 4, 2012 at 15:55
  • @Nomad There was a missing character at the end. Commented May 4, 2012 at 16:01
  • @miracle173 I'm not surprised there's a shorter answer. I didn't know about grep -l to be honest. I've updated my answer. Commented May 4, 2012 at 16:04
  • Guys thanks to everyone, who contributed to this question, for your time and help. I will tryout the suggestions and will come back here with update.
    – Nomad
    Commented May 4, 2012 at 18:19
3

grep's -c option is what you need here

find . -name \*txt | xargs grep -c -i "abc" | {
    total=0
    count=0 
    while IFS=: read name num; do 
        ((num > 0)) && ((count+=1))
        ((total+=num))
    done
    echo total=$total 
    echo count=$count
}

The braces to group the commands around the while loop are required to keep the variables in one scope for that subshell.

5
  • countcontains the number of files found by find . -name \*txt and not the number files containing the string because grep -c returng 0 if the string is not found in a file.
    – miracle173
    Commented May 5, 2012 at 4:03
  • @miracle173, quite right. answer updated. Commented May 5, 2012 at 11:49
  • @glenn jackman, your solution is very elegant and one stop approach. I like it. Plus it works :).
    – Nomad
    Commented May 8, 2012 at 3:30
  • @glenn jackman, your solution is great but for sometimes i was getting error for it, otherwise i would have accepted your answer. But thank you so much for your help and answer, really appreciate it.
    – Nomad
    Commented Aug 5, 2012 at 2:13
  • @Nomad, what errors? Commented Aug 5, 2012 at 14:19
3
$ grep -R --include='*.txt' -c -i abc . | awk -F: ' 
           BEGIN {
                    totalCount=0;noOfFiles=0;
                 } 
                 { totalCount=totalCount+$2; 
                   if ( $2 > 0 ) 
                   {
                       noOfFiles+=1;
                   } 
                 } 
            END {
             print "Total number of times abc appears:"totalCount; 
             print "Total number of files which has abc in it:"noOfFiles
            } '

(OR)

ls output should not be used to parsed by other programs. See the comment below.

$  ls -Rltr | awk '/.txt/{print $NF }' | xargs grep -c -i "abc" | awk -F: ' 
       BEGIN {
                totalCount=0;noOfFiles=0;
             } 
             { totalCount=totalCount+$2; 
               if ( $2 > 0 ) 
               {
                   noOfFiles+=1;
               } 
             } 
        END {
         print "Total number of times abc appears:"totalCount; 
         print "Total number of files which has abc in it:"noOfFiles
        } '


Result:
Total number of times abc appears:0
Total number of files which has abc in it:0
5
  • 1
    This command won't find text files in subdirectories (unless the directory has a .txt extension). Commented May 4, 2012 at 18:22
  • The answer that relies on ls -R doesn't work (the output of ls -R is not full paths! And retaining only the last word does nothing, except further mangle file name with spaces), and you shouldn't parse the output of ls anyway. Commented May 4, 2012 at 22:56
  • @anon_anon, i tried your both commands and they don't work. grep: illegal option -- R grep: illegal option -- include=*.txt Usage: grep -hblcnsviw pattern file . . . awk: syntax error near line 2 awk: bailing out near line 2
    – Nomad
    Commented May 7, 2012 at 2:03
  • @Gilles actually, I had an alias ls="ls -ltr", Hence the error. Fixed the issue, but as you said ls output should not be parsed. I didn't know that. Thanks!
    – user14039
    Commented May 7, 2012 at 14:02
  • @Nomad I am not exactly sure, but can you check if the following match? The command is working for me. $ shopt | grep 'on$' cmdhist on expand_aliases on extquote on force_fignore on hostcomplete on interactive_comments on progcomp on promptvars on sourcepath on
    – user14039
    Commented May 7, 2012 at 14:10
1

Number of abc's contained in files:

To count the number of all "abc"'s in the .txt files, use grep -c and find and - exceptionally - cat:

find . -name "*.txt" -exec cat {} + | grep -ic abc

Grep -c will do the total count for you - something I didn't find in SigueSigueBen's answer, which contains unjustified calls to xargs, imho. The other 2 answers where to long for me. I didn't study them and wouldn't write such things myself.

Number of files containing abc:

find . -name "*.txt" -exec grep -iq abc {} ";" -printf "1" | wc -c 

This will not fail with filenames (which are rarely, I admit) containing newlines in their name (which is perfectly legal).

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .