If-else-statement is working wrong in crontab?

Tag: bash , shell , if-statement , crontab Author: yz_hyg Date: 2014-01-29

When i use this:

*/5 6-18 * * 1-6 [ "$(ls -A /DIR_WHERE_FILES_ARE_OR_NOT/)" ] &&
  rsync -au /DIR_WHERE_FILES_ARE_OR_NOT/ /DIR_WHERE_FILES_SHOLD_GO; \
  mv /DIR_WHERE_FILES_ARE_OR_NOT/*  /SAVE_DIR/ ||
  mail -s "DIR IS EMPTY" [email protected] <<< "message"

i get two mails:

mv: cannot stat `/DIR_WHERE_FILES_ARE_OR_NOT/*': No such file or directory

and

"DIR IS EMPTY"

Why?

What about having a proper script with these conditions and calling it from crontab? Like this it is pretty complicated to trace and maintain.
Thanks! :) now i have changed the two commands in to { } but it doesnt help
Only if i ad exit 0; behind the two commends, i doent get any error mail BUT when files are in the DIR i get a error message: /bin/sh: -c: line 0: syntax error near unexpected token `}'

Other Answer1

You get

mv: cannot stat `/DIR_WHERE_FILES_ARE_OR_NOT/*': No such file or directory

for exactly the reason stated: that directory is empty, hence it does not contain a file named * (asterisk). It's just the way glob expansion works in the shell: if the glob doesn't match anything it is passed literally to the command. Since mv attemps to rename a non-existing file, it complains as shown.

This would all be much more readable, if instead of a sequence of && and || operators in a crontab you would place the whole logic in a script with equivalent if/else/fi constructs and just call the script from cron.

You get two mails because you explicitly send the first with mail -s. The second is from cron because the output on stderr and stdout is not empty.

Your commands are equivalent to

if [ "$(ls ...)" ]; then
   rsync
fi
if ! mv; then
   mail
fi

Note that there is no else.

comments:

But why crontab do not only the if-statement or the else statement?
Because of the precedence of && versus ||.
To clarify, because of the short-circuiting semantics in conjunction with the semicolon. In shell grammar parlance, the semicolon makes this two separate AND-OR-lists. In the second list, mv || mail, both are run because the mv fails.

Other Answer2

You get two mails because when mv fails, cron captures what is written to standard error and mails it to the owner, then runs the mail command. You can suppress the error message from mv to avoid the mail from cron.

mv /DIR_WHERE_FILES_ARE_OR_NOT/*  /SAVE_DIR/ 2> /dev/null || mail -s "DIR IS EMPTY" [email protected] <<< "message"

comments:

When mv fails, mv writes to stderr, not cron.
I knew I was wording that poorly, but did not realize just how poorly.