Bash Scripting – How to Use ‘if test’ and ‘find’ Commands Together

bashfindscriptingshell-scripttest

I have a directory with crash logs, and I'd like to use a conditional statement in a bash script based on a find command.

The log files are stored in this format:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

I want the if statement to only return true if there is a crash log for a specific app which has been modified in the last 5 minutes. The find command that I would use is:

find /var/log/crashes -name app-\*\.log -mmin -5

I'm not sure how to incorporate that into an if statement properly. I think this might work:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

There are a few areas where I'm unclear:

  • I've looked at the if flags but I'm not sure which one, if any, that I should use.
  • Do I need the test directive or should I just process against the results of the find command directly, or maybe use find... | wc -l to get a line count instead?
  • Not 100% necessary to answer this question, but test is for testing against return codes that commands return? And they are sort of invisible – outside of stdout / stderr? I read the man page but I'm still pretty unclear about when to use test and how to debug it.

Best Answer

[ and test are synonyms (except [ requires ]), so you don't want to use [ test:

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

test returns a zero exit status if the condition is true, otherwise nonzero. This can actually be replaced by any program to check its exit status, where 0 indicates success and non-zero indicates failure:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

However, all of the above examples only test against the program's exit status, and ignore the program's output.

For find, you will need to test if any output was generated. -n tests for a non-empty string:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

A full list of test arguments is available by invoking help test at the bash commandline.

If you are using bash (and not sh), you can use [[ condition ]], which behaves more predictably when there are spaces or other special cases in your condition. Otherwise it is generally the same as using [ condition ]. I've used [[ condition ]] in this example, as I do whenever possible.

I also changed `command` to $(command), which also generally behaves similarly, but is nicer with nested commands.

Related Question