Crontab command substitution problem

Hi all,

I am using Ubuntu 8.04 Linux. I have a cron job entry to run a shell script at a scheduled time. This script has a X application to be launched. As cron does not pick already exported environment variables and it runs in a separate environment, I need to export the DISPLAY variable to run my GUI process. So I am setting the DISPLAY environment variable through command substitution at the top of my script as follows:

export DISPLAY="`who am i | cut -d"(" -f2 | cut -d")" -f1`"

It seems, this does not work with cron (i.e) cron is not executing command substitution inside shell script or at the crontab's "command field" entry (i.e):

crontab -l output:

SHELL=/bin/bash

01 17 * * * export DISPLAY="`who am i | cut -d"(" -f2 | cut -d")" -f1`" && gui_process.sh &> error.log

I have no other option other than hardcoding the (literal) value instead of command substitution inside my script "gui_process.sh" as follows:

#!/bin/bash

export DISPLAY="xx.xx.xx.xx:0.0"
xterm +hold -e "GUI"

My questions are:

a) Why cron does not support "command substitution"?

b) Also, I have exported DISPLAY in my .bashrc file (since my login shell is bash) and I have specified this shell at the top of the crontab as: SHELL=/bin/bash. Still why cron is not reading it from ~/.bashrc while executing my script??

c) Since cron is not associated to any terminal (/dev/tty) I guess no need for trapping HUP signal?

Thank you

First off - ALL comands in a cron entry or in a cron script should have explicit paths to command files. The PATH is not the same as you have at the command line.

who am i can be implemented all kinds of ways, as an alias or some other feature of the who command. It is usually this:

/usr/bin/who am i

where "am and "i" are arguments.

You need to experiment with each simple element of your command in crontab entry - enter the command let it run, remove it from crontab, then check the log or email.

example

* * * * * /usr/bin/who am i >> /home/royalibrahim/logfile

wait a minute, remove the crontab entry and then read the logfile. Proceed iteratively.

Is there some reason not to use the shell variable $USER?

Th command "who am i" will fail when run from cron. It needs a terminal.
X Windows also needs a terminal.
What are you trying to achieve?

You can see the commands "xterm +hold -e", that I am launching xterm terminal to paint the X application.

  • cron never supported command substitution. It can only "support" it as far as the used shell (/bin/sh) supports it.
  • cron doesn't care for you .bashrc, or any other login file for that matter, because it won't start a login shell. What it will do is basically
    text /bin/sh -c "your command"
    at the specified time. As an example, this is the environment of a command started by cron on Linux:
    text SHELL=/bin/sh USER=pludi PATH=/usr/bin:/bin PWD=/home/pludi SHLVL=1 HOME=/home/pludi LOGNAME=pludi _=/usr/bin/env

    See the minimal path? That's all you have at the start of your script.
  • If you want to start an XTerm on a remote display for a certain user, you'll need to know where to connect to. But that's an information that cron couldn't possibly find out, as it will run even if you aren't connected. It will even run on Sundays, when (probably) no one is in the office. Where should the terminal be shown at then?

What if I am running only on my local machine (i.e) launching the xterm terminal in my local system - not on the remote systems?

So, command substitution is not possible in whatever be the case, right? what if scripts that are need to be run by cron, have the need of command substitution? How to achieve this? Is this a deficiency in cron - is that incapable to do that?

What is the full list of environment variables that can be set for crontab other than HOME,LOGNAME,PATH,SHELL,MAILTO variables?

cron doesn't care whether you're connected locally or from somewhere else, because it's primary design goal since it's creation was to run programs without any user interaction. That means no TTYs, no X displays by default, so it has no way of knowing what display you're running an X server at. You'll have to tell it that yourself, by whatever means (eg. writing the required DISPLAY variable to a file & reading that in your script)

As for substitution: no, cron doesn't know anything about that, because the shell can handle it just fine. The whole command you specify is passed, without any interpolation, to a shell, which then has to handle parameter & command expansion. Example: the line

* * * * * echo "$(date +'\%Y\%m\%dT\%H:\%M:\%S')" > /tmp/file

generates this file:

> cat /tmp/file
20091223T14:10:01

and this entry in the syslog

/usr/sbin/cron[11903]: (pludi) CMD (echo "$(date +'%Y%m%dT%H:%M:%S')" > /tmp/file)

You see, an expansion is done, just not by cron but by the shell it calls.

To find out the environment variables which are set when a job starts in the default shell for cron, create a one-off cron containing just an "env" command. The output from "env" will be in the mail for the user who owns the cron.

We're still not clear what you are trying to achieve but using "cron" (which is a background task scheduler) to try to start a process which needs a foreground terminal is unlikely to be satisfactory.

what you really want to do ( for best maintenance practices ) is not clutter up cron with a host of crazy commands.

Put all those crazy commands in a script somewhere, where you'll have control to debug at will. IE:

crontab file:

* * * * * /bin/ksh /home/quirkasaurus/bin/cron_env.sh /home/quirkasaurus/bin/rule_the_world.sh

Then my cron_env.sh sets up my full required environment,
and runs my script exactly in an environment I control completely, and not at the whims of however cron kicks things off.

My cron_env.sh is essentially:

. /home/quirkasaurus/.profile
$*