26

This question already has an answer here:

I am in a folder with lots of .txt files, I would like to find all the files which contain stringA but don't contain stringB (they are not necessarily in the same line). Does anyone know how to do this?

marked as duplicate by don_crissti, Jeff Schaller, Wouter Verhelst, G-Man, mdpc Jan 26 '18 at 1:08

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

26

As long as your filenames do not contain spaces, tabs, newline or wildcard characters, and if your grep supports the -L option, you can do it as follows:

$ cat file1
stringA
stringC
$ cat file2
stringA
stringB
$ grep -L stringB $(grep -l stringA file?)
file1

The grep executed in the subshell $(), will print all filenames which contain stringA. This filelist is input for the main grep command, which lists all files that do not contain stringB.

From man grep

  -v, --invert-match
          Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX.)

  -L, --files-without-match
          Suppress normal output; instead print the name of each input file from which no output would normally have been printed.  The scanning will stop on the first match.

  -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.)
  • Thank you... I have lots of files, your solution does give almost all the good results, but I see also several grep: alias: No such file or directory, do you know why? – SoftTimur May 8 '14 at 6:52
  • Do you have a spaces in your filenames? Or aliases defined for grep? – Bernhard May 8 '14 at 7:06
  • Sometimes there are spaces in the filenames... What should I do to cover them? – SoftTimur May 8 '14 at 7:07
  • @debai Sorry, I really don't want to change their filename, is there anything I can do on the level of the commands... – SoftTimur May 8 '14 at 7:12
  • @SoftTimur grep -L stringB $(grep -l stringA ls -l | tail -n+2 | awk '{print $NF}' | sed -e 's/\s/\\ /g' – debal May 8 '14 at 7:25
3

With GNU tools:

grep -lZ stringA ./*.txt |
  xargs -r0 grep -L stringB

-L, -Z, -r, -0 are GNU extensions sometimes but not always found in some other implementations.

0
#run loop for each file in the directory
for i in `ls -l | tail -n+2 | awk '{print $NF}'` ; do
   #check if file contains "string B" 
   #if true then filename is not printed
   if [[ `egrep "string B" $i | wc -l` -eq 0 ]] ; then
      #check if file contains "string A"
      #if false then file name is not printed
      if [[ `egrep "string A" $i | wc -l` -gt 0 ]] ; then
         #file name is printed only if "string A" is present and "string B" is absent
         echo $i
      fi
   fi
done

After checking Bernhard's answer:

grep -Le "string B" $(grep -le "string A" `ls`)

If file name contains spaces:

grep -L stringB $(grep -l stringA `ls -l | tail -n+2 | awk '{print $NF}' | sed -e 's/\s/\\ /g'`
  • Maybe someone else can give a better solution to your space problem, but this is all I could think of at the moment. :) Cheers – debal May 8 '14 at 7:30
  • 1
    People will complain that you are parsing ls :) – Bernhard May 8 '14 at 7:33
  • Thank you, but for the space problem, your commend still gives me errors like grep: def.txt: No such file or directory for filenames like abc def.txt. – SoftTimur May 8 '14 at 7:35

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