1

I want to search for 2 (or more) strings across the files of a certain directory and print the line numbers.

The search results should include only those files where all the mentioned strings are present (may or may not be in the same line). It should exclude files which contains either of the strings but not all of them.

What should be a convenient command to achieve this?

1

With GNU awk you can do:

awk 'BEGINFILE { n1=n2=0 }
     /str1/ { n1=FNR }
     /str2/ { n2=FNR }
     ENDFILE { if (n1&&n2) print FILENAME,n1,n2 }
' files...

A variant for a list of strings to consider:

awk -v s="str1 str2 str3" '
  BEGIN { n=split(s,str) ; m=(2^n)-1 }
  BEGINFILE { f=0 }
  { for (i=1; i<=n; i++)
    if ($0 ~ str[i]) {
      l[i] = FNR
      f += 2^(i-1)
    }
  }
  ENDFILE {
    if (f==m)
      for (i=1; i<=n; i++) print FILENAME,l[i]
  }
' files...

The commands are best put in a script file for execution, and replacing the files... list by "$@" to pass the files as arguments to the script.

A script, say "findall", to pass the directory (as asked in the comment) and search strings could be:

dir=${1:?}
shift
cd "$dir" || exit 1

awk -v s="$*" '
  ...as above...
' *

and coukd be called findall dir str1 str2 ... strN. (Note the search strings may not contain whitespace characters.)

  • Can I specify a directory instead of a list of files? – Siddhartha Ghosh Jun 4 '15 at 8:44
  • Yes, of course. If you want all files in a directory dir processed then just replace files... by dir/*. – Janis Jun 4 '15 at 8:52
  • It is not working if I have nested directories inside dir/ – Siddhartha Ghosh Jun 4 '15 at 9:11
  • Well, if you want a whole directory hierarchy traversed then you must use find -type f to obtain the filenames. And to call commands (like awk) to work on the files, use something like, e.g.: find -type f | xargs awk '...'. – Janis Jun 4 '15 at 9:15
0

One method is to first list the matching files, then read the files again to find the desired lines. This is efficient as long as the matching files aren't too large or too numerous.

Assuming recent enough GNU utilities (Linux/Cygwin) to avoid problems with file names containing special characters:

grep -Rlz -Fe "foo" . |
xargs -0 grep -lz -Fe "bar" /dev/null |
xargs -0 grep -lz -Fe "qux" /dev/null |
xargs -0 awk '/foo|bar|qux/ {print FNR}' /dev/null

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.