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
-Z
but it has the--null
long-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 directory
for 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\n
is 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-r
passed to xargs, which does not run the command if no arguments are availablels
like that is a bad idea.find
is the standard way – phuclv May 27 '23 at 02:21