Python does not get environment variable when using cron.

Cameron Simpson cs at zip.com.au
Mon Aug 18 01:11:59 EDT 2008


On 17Aug2008 21:25, John Nagle <nagle at animats.com> wrote:
> Stephen Cattaneo wrote:
>> I am attempting to execute an automated test (written in Python) via
>> cron.  I have to check the HOSTNAME variable as part of the test, oddly
>> under cron the HOSTNAME environment variable is not in the os.environ
>> dictionary.  I know that cron runs in a subshell that does not have all
>> of the normally set environment variables.  HOSTNAME is not one of those
>> variables, it is set even in cron's subshell.  Why doesn't python get
>> this variable?  Is this a bug in python2.4?  

Because $HOSTNAME is a bash specific variable, set by bash but NOT
EXPORTED! Like $0 and a bunch of other "private" variables, subprocesses
do not inherit this value. From "man bash":


   Shell Variables
       The following variables are set by the shell:
       [...]
       HOSTNAME
              Automatically set to the name of the current host.

Note that "set" does not imply "exported". Only exported vairables are
seen by subprocesses.

>    Cron doesn't normally use a shell at all.  It just runs the
> requested program in a subprocess. So there's no shell involved,
> and you don't get a shell-type user environment.

This statement is false.

Cron hands _all_ jobs to a shell. From "man 5 crontab":

   The ``sixth'' field (the rest of the line) specifies the command to be
   run.  The entire command portion of the line, up to a newline or % char-
   acter, will be executed by /bin/sh or by the shell specified in the SHELL
   variable of the cronfile. 

That's from Vixie cron, but all UNIX crons hand the command part of the
cron line to a shell, usually /bin/sh.

You're probably confused by the fact that cron does not invoke "login" shells
(with their associated initialisation from /etc/profile and $HOME/.profile).

>  If you have
> a crontab line like
> 10 3 * * * /usr/bin/python someprogram.py
> there's no shell.

Not so. A shell will be interpreting the string "/usr/bin/python
someprogram.py", mch as happens when you type this on the command line.


>  You can try
> 10 3 * * * /bin/sh /usr/bin/python someprogram.py
>    which will load a shell, which in turn will load Python.

Even more not so. The command "/bin/sh /usr/bin/python someprogram.py"
will attempt to parse the file "/usr/bin/python" as though it were a
shell script. That's almost certainly not what you wanted.

To do what you describe requires the line:

  /bin/sh -c '/usr/bin/python someprogram.py'

which hands the command string "/usr/bin/python someprogram.py" to
/bin/sh for interpretation. Were you to do this as a cron line, the
string: "/bin/sh -c '/usr/bin/python someprogram.py'" would be handed
to /bin/sh for interpretation. That interpreter in turn then hands
"/usr/bin/python someprogram.py" to /bin/sh for interpretation.

A parent-child nested process listing (with some fake quoting tossed in
to show the strings) would look like this:

  /bin/sh -c "/bin/sh -c '/usr/bin/python someprogram.py'"
    /bin/sh -c '/usr/bin/python someprogram.py'
      /usr/bin/python someprogram.py

i.e. three process alive.

> Or, in Python, you can use "socket.gethostname()", which will
> get you the host name used for networking purposes.

Or, on a cron line (after the time fields, omitted here):

  HOSTNAME=`hostname`; export HOSTNAME; python someprogram.py

Cheers,
-- 
Cameron Simpson <cs at zip.com.au> DoD#743
http://www.cskk.ezoshosting.com/cs/

186,282 miles per second - Not just a good idea, It's the Law!



More information about the Python-list mailing list