Home > OS >  How to search "hello" but exclude "test" from find grep files
How to search "hello" but exclude "test" from find grep files

Time:01-15

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.

  • Related