Coder10
Coder10

Reputation: 65

Printing file name of with a string

Goal: is to print the name of every .c program in a directory specified by first argument that contains some word.

My idea was:

word = "word"
for file in specifiedDirectory with c file extension
do 
if grep -w $word $file ; then
echo $file
fi
done

Upvotes: 2

Views: 167

Answers (3)

Etan Reisner
Etan Reisner

Reputation: 80931

With a modern find the following should do what you want.

wordfind() {
    find "$1" -name '*.c' -exec grep -wl word {} \+
}

You could also spit all the files out and use xargs to try to cut down the number of times grep needs to be run.

Something like this.

wordfind() {
    find "$1" -name '*.c' -print0 | xargs -0 -r grep -wl word
}

Use "$@" in place of "$1" in either of the above functions to support passing multiple directories to the functions instead of just one directory. (Also adds support for supplying arbitrary arguments to find but that's a different topic entirely.)

Upvotes: 1

Reinstate Monica Please
Reinstate Monica Please

Reputation: 11593

If you have GNU grep you can also just do

grep -rwl --include '*.c' word specifiedDirectory

With relevant options from man page

-r, --recursive
      Read  all  files  under  each directory, recursively, following symbolic links only if they are on the command line.
      This is equivalent to the -d recurse option.
-w, --word-regexp
      Select  only  those  lines  containing  matches that form whole words.  The test is that the matching substring must
      either be at the beginning of the line, or preceded by a non-word constituent  character.   Similarly,  it  must  be
      either  at  the  end  of  the line or followed by a non-word constituent character.  Word-constituent characters are
      letters, digits, and the underscore.
-l, --files-with-matches
      Suppress  normal  output;  instead  print  the  name  of  each input file from which output would normally have been
      printed.  The scanning will stop on the first match.  (-l is specified by POSIX.)
--include=GLOB
      Search only files whose base name matches GLOB (using wildcard matching as described under --exclude).

Upvotes: 3

John1024
John1024

Reputation: 113864

shopt -s globstar
word="word"
for file in path/to/**/*.c
do 
    grep -l -w "$word" "$file"
done

Notes:

  • To avoid unpleasant surprises, always put shell variables, like word or file in double-quotes as shown above. Tnis prevents word-splitting.

  • With the -l option, grep will print the name of any matching file. This makes the if and echo statements unnecessary.

  • In order to search a whole directory tree, we use bash's globstar feature. This enables **/ to match zero or more directories. (On Mac OSX, this feature is not available unless you have upgraded to bash version 4.0 or better.)

  • In bash assignment statements, there can be no spaces on either side of the equal sign. Let's consider how bash looks at the line:

    word = "word"
    

    As bash interprets that line, it will try to execute the command word with two arguments: = and word. If that is not what you want, the spaces must be removed.

Further simplification

grep will accept multiple filenames on its command line. So, there is no need for the for loop:

shopt -s globstar
word="word"
grep -l -w "$word" path/to/**/*.c

Enhancement: providing the directory to search on the command line

Suppose we want to run the script as ./word someDirectory and have it search the someDirectory tree for the c files. In the script that the first argument can be referenced as $1. So, create a file named goto which its execute bit set (chmod +x word) with the contents:

#!/bin/bash
shopt -s globstar
word="word"
for file in "$1"/**/*.c
do 
    grep -l -w "$word" "$file"
done

Likewise for the simplified version:

#!/bin/bash
shopt -s globstar
word="word"
grep -l -w "$word" "$1"/**/*.c

Upvotes: 1

Related Questions