How can I grep for files in a directory structure that have this text


but are missing this text



Note: I mean the directory the script starts in and all subdirectories.

  • Hmm, in the main dir, or main dir and subdirectories? – Rich Homolka Apr 3 '13 at 18:59
  • I mean the dir we start the script in and all subdirectories. Sorry that wasn't clear before. – Dan Rosenstark Apr 3 '13 at 20:17

Here's one that is loosely based on Rich Homolka's answer, but works on directory trees:

find . -type f  -exec grep -l addDesignControlChangeNotification {} \; | 
 while IFS= read -r file; do 
   grep -q removeDesignControlChangeNotification "$file" > /dev/null ; 
   [ $? -ne 0 ] && echo $file; 

Any files returned will contain addDesignControlChangeNotification but not removeDesignControlChangeNotification.


  • find . -type f -exec grep -l foo {} \;: This will print all files in any subdirectory of the current directory that contain the string foo. The -l flag causes grep to only print the names of matching files.

  • while read file : this iterates through each file found above, saving its name in the variable $file.

  • grep -q bar "$file" > /dev/null : this comand loks for the string bar in each of the files that contained foo.

  • [ $? -ne 0 ] && echo $file; : If the return value ($?) of the grep command was 0 (i.e. if the string was not in the file), print the name of the file.

  • Awesome, I'm using find . -name '*.m' -type f -exec grep -l addDesignControlChangeNotification {}... which I added myself since I didn't mention it in the question ;) Thanks! – Dan Rosenstark Apr 3 '13 at 20:49

this should work:

grep -l $FIRST * | while IFS= read -r FILE
    grep $SECOND "$FILE" &> /dev/null
    if [ $? -ne 0 ]
        echo "File $FILE has $FIRST but not $SECOND"
  • Looks great, but I'm getting "Is a directory" instead of actually entering into those directories. Any idea what's wrong? Thank you +1! – Dan Rosenstark Apr 3 '13 at 20:17
  • @Yar, you can add an -r to the first grep command if you want to recurse directories. Or you can use my answer ;). – terdon Apr 3 '13 at 20:29

One liner:

comm -2 -3 <(grep -rl addDesignControlChangeNotification     . | sort ) \
           <(grep -rl removeDesignControlChangeNotification  . | sort )

grep -r is a recursive grep, <() is process substitution, and comm shows you lines that a pair of (sorted) files do/don't have in common. In this case we only want the column 1 output: lines in the first file (where "file" is really grep output) which are not in the second.

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.