4

I need a way to tell if grep does find something, and ideally pass that return value to an if statement.

Let's say I have a tmp folder (current folder), in that there are several files and sub-folders. I want to search all files named abc for a pattern xyz. The search is assumed to be successful if it finds any occurrence of xyz (it does not matter how many times xyz is found). The search fails if no occurrence of xyz is found.

In bash, it can be done like this:

find . -name "abc" -exec grep "xyz" {} \;

That would show if xyz is found at all. But I'm not sure how pass the result (successful or not) back to an if statement.

Any help would be appreciated.

artm
  • 17,291
  • 6
  • 38
  • 54
  • 1
    Note that your example shows usage of the `find` command, and has nothing in particular to do with bash. Where is your `if` statement, and how do you want to use the output of your search? – ghoti Jul 30 '15 at 00:15
  • 1
    Are you looking to know about the result for each file separately, or collectively (the difference between "does every file `abc` contain the text `xyz`" and "does at least one file `abc` contain the text `xyz`")? – Jonathan Leffler Jul 30 '15 at 00:16
  • Collectively - thanks – artm Jul 30 '15 at 00:16

5 Answers5

3

You can try

x=`find . -name abc | xargs grep xyz`
echo $x

That is, x contains your return value. It is blank when there is no match.

Alain
  • 532
  • 6
  • 15
  • Just to be clear, the `echo x` part is just for you to test. Then you need to figure out how to use an if statement in bash, which should be easy for you to find out. If not then you can ask about this topic. – Alain Jul 30 '15 at 00:15
  • Right, then checking if the results is empty or not seems to do the trick for this case: http://stackoverflow.com/questions/3061036/how-to-find-variable-is-empty-or-in-shell-script – artm Jul 30 '15 at 00:45
  • This is somewhat unsophisticated, and needlessly collects the list of matching files. If you really want to know which files matched, piping the output from `find -exec grep` to a loop over those file names is usually more natural. – tripleee Jul 30 '15 at 03:43
3

If you want to know that find finds some files abc and that at least one of them contains the string xyz, then you can probably use:

if find . -name 'abc' -type f -exec grep -q xyz {} +
then : All invocations of grep found at least one xyz and nothing else failed
else : One or more invocations of grep failed to find xyz or something else failed
fi

This relies on find returning an exit status for its own operations, and a non-zero exit status of any of the command(s) it executes. The + at the end groups as many file names as find thinks reasonable into a single command line. You need quite a lot of file name (a large number of fairly long names) to make find run grep multiple times. On a Mac running Mac OS X 10.10.4, I got to about 3,000 files, each with about 32 characters in the name, for an argument list of just over 100 KiB, without grep being run multiple times. OTOH, when I had just under 8000 files, I got two runs of grep, with around 130 KiB of argument list for each.

Someone briefly left a comment asking whether the exit status of find is guaranteed. The answer is 'yes' — though I had to modify my description of the exit status a bit. Under the description of -exec, POSIX specifies:

If any invocation [of the 'utility', such as grep in this question] returns a non-zero value as exit status, the find utility shall return a non-zero exit status.

And under the general 'Exit status' it says:

The following exit values shall be returned:

0 — All path operands were traversed successfully.
>0 — An error occurred.

Thus, find will report success as long as none of its own operations fails and as long as none of the grep commands it invokes fails. Zero files found but no failures will be reported as success. (Failures might be lack of permission to search a directory, for example.)

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

find returns the result of the -exec'd command as its result, just place the command in an if statement:

if [[ -n $(find . -name "abc" -exec grep "xyz" {} \;) ]]
then
   # do something
fi
Hunter McMillen
  • 59,865
  • 24
  • 119
  • 170
  • 1
    @artm There is a better explanation here than I could provide: http://serverfault.com/questions/52034/what-is-the-difference-between-double-and-single-square-brackets-in-bash. Are you pointing to /bin/sh? You said bash that is why I used `[[ .. ]]`. – Hunter McMillen Jul 30 '15 at 00:41
1

The grep command can be given a -q option that will "quiet" its output. Grep will return a success or a failure on the basis of whether it found anything in the file you pointed it at.

If your shell is capable of it, rather than trying to parse the output of a find command, you might want to try using a for loop instead. For example in bash:

shopt -s globstar

cd /some/directory

for file in **/abc; do
  grep -q "xyz" "$file" && echo "Found something in $file"
done

Is this what you're looking for?

ghoti
  • 45,319
  • 8
  • 65
  • 104
1

You should be able to do this with just grep with --include I believe.

Like this:

if grep -r --include=abc -q xyz; then
    : Found at least one match
else
    : No matching lines found
fi
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • thanks - seems to work on normal grep; unfortunately the grep version in busybox doesn't support either option `-r` or `--include` – artm Jul 30 '15 at 02:29