1

I am trying to add following command to crontab:

I=1; for X in $(/bin/ls -r /var/tmp/*); do [ $((I++)) -le 28 ] && echo "lower" || echo "higher"; done

When executed on the command line (in bash), the command works fine. But when I add the line into crontab and when executed, cron complains:

/bin/sh: 1: arithmetic expression: expecting primary: "I++"

Do I need to use different syntax in cron ?

EDIT1: I have replaced sh with bash in my /etc/crontab:

SHELL=/bin/bash

I have restarted cron, but following cron line still does not execute:

(I=1; for X in $(/bin/ls -r /var/tmp/*); do [ $((I++)) -le 28 ] && echo "lower" || echo "higher"; done)

the error suggests, that it is still being interpreted with /bin/sh instead of /bin/bash:

/bin/sh: 1: arithmetic expression: expecting primary: "I++"
3

Cron, if I'm not mistaken, defaults to /bin/sh. Check /etc/crontab/ for the line SHELL=. It is likely set to /bin/sh (dash). I believe you can set SHELL=/bin/bash in your own user crontab file (the one edited by crontab -e). Or you can script it.

  • I have SHELL=/bin/sh in my /etc/crontab. Is there any reason, why I should want to use /bin/sh, instead of /bin/bash ? I am on Debian, and the system shell is bash. So why would they keep sh for cron. AFAIK, bash is compatible with sh, but not the other way around. – Martin Vegter Sep 30 '13 at 7:38
  • Nope, there is no reason to keep it set to /bin/sh AFAIK. I think it is just a default setting. – AGHz Sep 30 '13 at 11:35
3

Yes, you need to use sh syntax. You can find the specification for the portable standard sh syntax there.

In this case, you want [ "$((i+=1))" -le 29 ], or if your sh is based on an older version of ash that did not support the full set of standard arithmetic operators yet, use [ "$i" -le 28 ] ...; i=$(($i + 1)).

A note of warning: processing a world writeable directory like /var/tmp under cron is like opening a can of worm wrt security.

You've got to treat filenames and their type in there as untrusted and extremely dirty.

Your $(/bin/ls -r /var/tmp/*) in particular is bogus as it makes a lot of assumptions on what characters the filenames may contain and assumes none of them are directories or symlinks to directories.

  • when I use [ "$((I+=1))" -le 28 ], cron complains: /bin/sh: 1: Bad substitution – Martin Vegter Sep 29 '13 at 19:41
2

To make this completely portable you can replace the $() syntax with:

I=1; for X in `/bin/ls -r /var/tmp/*`; do [ $I -le 28 ] && echo "lower" || echo "higher"; I=`expr $I + 1`; done

But beware of cron jobs and world writable directories.

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.