-2

Let's say I have multiple files with .ext extension containing multiple lines. I need to print filenames containing all 3 keywords: kwd1, kwd2, and kwd3.

How do I do it?

  • 2
    Are you at all interested in the content of the files, or do you just want to match the keywords against the filenames? Saying "I want to print the filenames containing the keywords" implies that you are only interested in the filenames. – Kusalananda Apr 18 at 13:51
  • 1
    Is it the filename or the file content that is multi-line? Please improve question, as it is currently hard to know what you are asking. – ctrl-alt-delor Apr 18 at 13:52
6

To find all filenames ending in .ext and containing the three keywords kwd1, kwd2 and kwd3, anywhere in or below the current directory:

find . -name '*.ext' -name '*kwd1*' -name '*kwd2*' -name '*kwd3*'

Or, setting the keywords in a more dynamic way,

set -- "kwd1" "kwd2" "kwd3"

for word do
    set -- "$@" -name "*$word*"
    shift
done

find . -name "*.ext" "$@" -print

Or, if you want to search for the keywords inside the files:

set -- "kwd1" "kwd2" "kwd3"

for word do
    set -- "$@" -exec grep -q -wF -e "$word" {} ';'
    shift
done

find . -name "*.ext" "$@" -print

I'm using -wF with grep here to only do a string comparison (-F) of whole words (-w) in the files.

In a shell supporting named arrays, that last bit of code might look like

keywords=( "kwd1" "kwd2" "kwd3" )

and_expr=()
for word in "${keywords[@]}"; do
    and_expr+=( -exec grep -q -wF -e "$word" {} ';' )
done

find . -name "*.ext" "${and_expr[@]}" -print
1

Use

find . -type f -name "*.ext" -exec grep -q kwd1 {} \; -exec grep -q kwd2 {} \; -exec grep -l kwd3 {} \;
1

Using recursive grep and read:

grep --include '*.ext' -lr 'kwd1' . | 
while read -r file; do grep -m1 -q 'kwd2' "$file" && grep -m1 -l 'kwd3' "$file"; done

grep --include '*.ext' -lr 'kwd1' . - Will find all files (recursively) in the current directory that end in .ext and contain kwd1

The while loop will then take each file containing kwd1 and confirm that it also has both kwd2 and kwd3

Note: This will break on filenames containing newlines.

0

You can run a find command to identify all the target files and then run a grep on those to detect the files.

$ find . -name '*.ext' -type f -exec grep -Pqz '(?s:(?=.*kwd1.*)(?=.*kwd2.*)(?=.*kwd3.*))' \{\} \; -print 
-1

I tested with below method and it worked fine:

for i in file file2...filen; do grep "kwd1" $i; if [[ $? == 0 ]]; then grep "kwd2" $i; if [[ $? == 0 ]]; then grep "kwd3" $i; if [[ $? == 0 ]]; then echo "file is $i"; fi; fi; fi; done
  • Can you please explain what this is doing, we generally prefer longer form answers on this site. Code with no explanation is nice but answers with more specifics are best. – slm Apr 19 at 1:16
  • it will print all filenames which consists of above mentioned 3 strings. it will check for first string then proceed for second string then 3rd string – Praveen Kumar BS Apr 21 at 15:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.