How can I use bash's if test and find commands together?
[
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.
find
will exit successfully if there weren't any errors, so you can't count on its exit status to know whether it found any file. But, as you said, you can count how many files it found and test that number.
It would be something like this:
if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
...
fi
test
(aka [
) doesn't check the error codes of the commands, it has a special syntax to do tests, and then exits with an error code of 0 if the test was successful, or 1 otherwise. It is if
the one that checks the error code of the command you pass to it, and executes its body based on it.
See man test
(or help test
, if you use bash
), and help if
(ditto).
In this case, wc -l
will output a number. We use test
's option -gt
to test if that number is greater than 0
. If it is, test
(or [
) will return with exit code 0
. if
will interpret that exit code as success, and it will run the code inside its body.
This would be
if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then
or
if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then
The commands test
and [ … ]
are exactly synonymous. The only difference is their name, and the fact that [
requires a closing ]
as its last argument. As always, use double quotes around the command substitution, otherwise the output of the find
command will be broken into words, and here you'll get a syntax error if there is more than one matching file (and when there are no arguments, [ -n ]
is true, whereas you want [ -n "" ]
which is false).
In ksh, bash and zsh but not in ash, you can also use [[ … ]]
which has different parsing rules: [
is an ordinary command, whereas [[ … ]]
is a different parsing construct. You don't need double quotes inside [[ … ]]
(though they don't hurt). You still need the ;
after the command.
if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then
This can potentially be inefficient: if there are many files in /var/log/crashes
, find will explore them all. You should make find stop as soon as it finds a match, or soon after. With GNU find (non-embedded Linux, Cygwin), use the -quit
primary.
if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then
With other systems, pipe find
into head
to at least quit soon after the first match (find will die of a broken pipe).
if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then
(You can use head -c 1
if your head
command supports it.)
Alternatively, use zsh.
crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then