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.7k times
46
don_crissti
- 79,330
- 30
- 216
- 245
SoftTimur
- 657
- 1
- 6
- 6
3 Answers
47
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
- 522,931
- 91
- 1,010
- 1,501
Bernhard
- 11,992
- 4
- 59
- 69
-
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 08 '14 at 06:52
-
Do you have a spaces in your filenames? Or aliases defined for grep? – Bernhard May 08 '14 at 07:06
-
Sometimes there are spaces in the filenames... What should I do to cover them? – SoftTimur May 08 '14 at 07: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 08 '14 at 07:12
-
@SoftTimur grep -L stringB $(grep -l stringA `ls -l | tail -n+2 | awk '{print $NF}' | sed -e 's/\s/\\ /g'` – debal May 08 '14 at 07:25
-
Sorry, how could i merge this with `grep -L stringB $(grep -l stringA file?)`? – SoftTimur May 08 '14 at 07:25
-
@SoftTimur Please check my answer for proper formatting of the code.. :) – debal May 08 '14 at 07:27
-
1A whitespace-safe version should be something like `while read -rd $'\0' file; do grep -L 'stringB' "$file"; done < <(find . -type f -exec grep -Zl 'stringA' {} \;)` – steeldriver May 08 '14 at 19:48
-
@SoftTimur - Use his command and just precede it with: `IFS='\n' ;... ` where the `\n` is a literal newline. That will handle the spaces problem anyway. – mikeserv Jun 05 '14 at 19:22
-
useful to know if you use std::vector without (#)include
`grep -L " – fiorentinoing Apr 20 '18 at 14:46" $(grep -rl std::vector *) | grep -v .so$ | grep -v .o$` -
If the subshell returns no results, grep will wait forever since there is no output. I suggest using `grep -lR 'wantedString' . | xargs -r grep -L 'non-wanted-string'` The key being `-r` passed to xargs, which does not run the command if no arguments are available – Cec May 23 '22 at 07:03
-
@debal [piping `ls` like that is a bad idea](https://unix.stackexchange.com/q/128985/44425). `find` is the standard way – phuclv May 27 '23 at 02:21
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
- 522,931
- 91
- 1,010
- 1,501
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,664
- 5
- 17
- 18
-
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 like `abc def.txt`. – SoftTimur May 08 '14 at 07:35