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?
Asked
Active
Viewed 2.9k times
49
don_crissti
- 82,805
SoftTimur
- 687
3 Answers
49
As long as your filenames do not contain spaces, tabs, newline (assuming an unmodified $IFS) or wildcard characters and don't start with -, 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.)
Stéphane Chazelas
- 544,893
Bernhard
- 12,272
6
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.
Stéphane Chazelas
- 544,893
-
For macOS:
brew install grep, then use ggrep instead of grep. brew is a nonstandard command, it is "homebrew". – artyom.razinov Dec 14 '23 at 13:38 -
@artyom.razinov, actually as grep used to be GNU grep on FreeBSD (on which macos userland tools are based), it has kept a lot of the GNU grep API. IIRC it doesn't have
-Zbut it has the--nulllong-option equivalent. – Stéphane Chazelas Dec 14 '23 at 13:58
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'`
debal
- 3,704
-
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 08 '14 at 07:30
-
1
-
Thank you, but for the space problem, your commend still gives me errors like
grep: def.txt: No such file or directoryfor filenames likeabc def.txt. – SoftTimur May 08 '14 at 07:35
grep: alias: No such file or directory, do you know why? – SoftTimur May 08 '14 at 06:52ls -l | tail -n+2 | awk '{print $NF}' | sed -e 's/\s/\\ /g'– debal May 08 '14 at 07:25grep -L stringB $(grep -l stringA file?)? – SoftTimur May 08 '14 at 07:25while read -rd $'\0' file; do grep -L 'stringB' "$file"; done < <(find . -type f -exec grep -Zl 'stringA' {} \;)– steeldriver May 08 '14 at 19:48IFS='\n' ;...where the\nis a literal newline. That will handle the spaces problem anyway. – mikeserv Jun 05 '14 at 19:22grep -L "<vector>" $(grep -rl std::vector *) | grep -v .so$ | grep -v .o$grep -lR 'wantedString' . | xargs -r grep -L 'non-wanted-string'The key being
– Cec May 23 '22 at 07:03-rpassed to xargs, which does not run the command if no arguments are availablelslike that is a bad idea.findis the standard way – phuclv May 27 '23 at 02:21