I want to search
all files include text "hello
" but exclude
the result contains "test
".
Here is the example files:
mkdir -p /tmp/test
cd /tmp/test
echo "foo hello" > foo.txt
echo "bar world" > bar.txt
echo "test hello" >> bar.txt
echo "world hello" >> bar.txt
Here is the search for "hello":
# find /tmp/test -type f -name '*' -exec grep -H -i "hello" {} \;
/tmp/test/bar.txt:test hello
/tmp/test/bar.txt:world hello
/tmp/test/foo.txt:foo hello
Now I want to exclude "test" from the above search output:
# find /tmp/test -type f -name '*' -exec grep -H -i "hello" {} \; | grep -v "test"
...Nothing here...
Try other pattern:
# find /tmp/test -type f -name '*' -exec grep -H -i "hello" -v "test" {} \;
grep: test: No such file or directory
/tmp/test/bar.txt:bar world
grep: test: No such file or directory
Here is the expected output:
# find /tmp/test -type f -name '*' -exec grep [commands argumensts here] {} \;
/tmp/test/bar.txt:world hello
/tmp/test/foo.txt:foo hello
How to do this search
and exclude
for find in files?
CodePudding user response:
The issue is that you have test
in the file path.
You can match for things after the :
but that will only work if you do not have :
in the text of the file.
find /tmp/test -type f -name '*' -exec grep -H -i
Example:
/t/test ❯❯❯ find /tmp/test -type f -name '*' -exec grep -H -i "hello" {} \; | grep -v -E ".*\:.*test"
/tmp/test/foo.txt:foo hello
/tmp/test/bar.txt:world hello
CodePudding user response:
You can use awk which can parse file contents very quickly. The following sample script
#!/bin/sh
file1="grepTest1.txt"
echo "dum dum
dum hello dum
crumb test crumb
dum dum" >${file1}
file2="grepTest2.txt"
echo "dum dum
dum hello dum
dum dum" >${file2}
file3="grepTest3.txt"
echo "dum dum
dum test dum
crumb hello crumb
dum dum" >${file3}
file4="grepTest4.txt"
echo "dum dum
crumb jello crumb
crumb bellow crumb
dum test dum
dum dum" >${file4}
for file in grepTest?.txt
do
awk -v acc="hello" -v rej="test" 'BEGIN{
good=0 ;
}
{
if( index($0, rej) != 0 ){
good=2 ;
exit ;
}else{
if( index($0, acc) != 0 ){
good=1 ;
line=$0 ;
} ;
} ;
}
END{
if( good == 1 ){
printf("%s: %s\n", FILENAME, line ) ;
}else{
if( good == 2 ){
printf("REJECT: Found %s at line %s in %s.\n", rej, NR, FILENAME ) | "cat >&2" ;
} ;
} ;
}' ${file}
done
gives the output
REJECT: Found test at line 3 in grepTest1.txt.
grepTest2.txt: dum hello dum
REJECT: Found test at line 2 in grepTest3.txt.
REJECT: Found test at line 4 in grepTest4.txt.
BUT ... note that the 3 reject lines go to stderr. if you redirect stdout to a file, you will only have the list of good files in that list.