Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

Perl/UNIX .profile problem

by Ronnie (Scribe)
on Nov 18, 2004 at 10:20 UTC ( [id://408734] : perlquestion . print w/replies, xml ) Need Help??

Ronnie has asked for the wisdom of the Perl Monks concerning the following question:

Can anyone answer the following puzzler - I have a work-round but the reason/cause for this problem is intriguing me. A former colleague wrote a Perl Module called which is widely used to transfer files from server to server etc. Part of the requirement for running this script is to have the following in your calling script -
use lib "$ENV{'SECTRAN_DIR'}/lib" ; use sectran ;
In most users this works but our production user - interface - didn't have the SECTRAN_DIR environment variable set, after correcting this any script calling sectran works. But only at the command line. When trying to run scripts via the interface cron they fail as the SECTRAN_DIR variable is not being found. Initially I thought this was the old chestnut about the interface cron not calling the local .profile so I included said call in my script.
if (exists $ENV{HOME}) { $file = "$ENV{HOME}/.profile" ; if (! -x $file) { print "\n\tUnable to execute the $file from the current user!\n" +; print "\n\tInsufficient permissions!\n" ; $result = 99 ; } else { open INP,"<$file" or die "\n\tfitz yer gem min?" ; while (<INP>) { print $_ } close INP or die "\n\tA canny dee it min!\n" ; # print "\n\tRunning $file.\n" ; $result = system "$file" ; print "\n\tResult set to $result\n" ; } # } else { print "\n\tNo profile file found!\n" ; $result = 98 ; }
This prints out the .profile file that it has executed -
TERM=vt220; export TERM PATH=/usr/local/bin:/usr/bin:/usr/ucb:/etc:/isw/tpp/oracle/live/1.6.1/ +ids/bin:/i sw/tpp/oracle/live/1.6.1/ids/forms60/mesg:/isw/appwork/live/finance/gu +ifmxs:. EDITOR=vi ;export EDITOR umask 002 ORACLE_TERM=vt220 ; export ORACLE_TERM ORACLE_SID=ISWLIVE ; export ORACLE_SID ORACLE_HOME=/isw/tpp/oracle/live/8.1.7; export ORACLE_HOME PATH=$PATH:${ORACLE_HOME}/bin:${ORACLE_HOME}/dbs export PATH LD_LIBRARY_PATH=${ORACLE_HOME}/lib:/usr/lib:/usr/ucblib export LD_LIBRARY_PATH PS1='${ORACLE_SID} > ' PS2='?' SECTRAN_DIR=/home/interface/sectran export SECTRAN_DIR TWO_TASK=isw-live; # Addition to allow ORACLE Forms testing in TEST & Other environments +- Bob alias ISWTEST='ORACLE_SID=ISWTEST ; export ORACLE_SID; \ ORACLE_HOME=/isw/tpp/oracle/test/8.1.7; export ORACLE_HOME \ LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib:/usr/ucblib export LD_LIBRARY_PATH PATH=/usr/local/bin:/usr/bin:/usr/ucb:/etc:/isw/tpp/oracle/test/1.6.1/ +idst/bin:/ isw/tpp/oracle/test/1.6.1/idst/forms60/mesg:/isw/appwork/test/finance/ +guifmxs:. PATH=$PATH:${ORACLE_HOME}/bin:${ORACLE_HOME}/dbs export PATH' # ISWLIVE alias ISWLIVE='ORACLE_SID=ISWLIVE ; export ORACLE_SID; \ ORACLE_SID=ISWLIVE ; export ORACLE_SID ORACLE_HOME=/isw/tpp/oracle/live/8.1.7; export ORACLE_HOME LD_LIBRARY_PATH=${ORACLE_HOME}/lib:/usr/lib:/usr/ucblib export LD_LIBRARY_PATH PATH=/usr/local/bin:/usr/bin:/usr/ucb:/etc:/isw/tpp/oracle/live/1.6.1/ +ids/bin:/i sw/tpp/oracle/live/1.6.1/ids/forms60/mesg:/isw/appwork/live/finance/gu +ifmxs:. PATH=$PATH:${ORACLE_HOME}/bin:${ORACLE_HOME}/dbs export PATH' # ISWTEACH alias ISWTEACH='ORACLE_SID=ISWTEACH ; export ORACLE_SID; \ ORACLE_SID=ISWTEACH ; export ORACLE_SID ORACLE_HOME=/isw/tpp/oracle/general/8.1.7; export ORACLE_HOME LD_LIBRARY_PATH=${ORACLE_HOME}/lib:/usr/lib:/usr/ucblib export LD_LIBRARY_PATH PATH=/usr/local/bin:/usr/bin:/usr/ucb:/etc:/isw/tpp/oracle/general/1.6 +.1/ids/bin :/isw/tpp/oracle/general/1.6.1/ids/forms60/mesg:/isw/appwork/general/f +inance/gui fmxs:. PATH=$PATH:${ORACLE_HOME}/bin:${ORACLE_HOME}/dbs export PATH'
Bear with me as we are getting to the crux of the problem now.This made no difference - SECTRAN_DIR still did not exist. I then added code to print out the ENV hash which I foolishly thought should reflect the .profile listed -
# while (($key, $value) = each (%ENV)) { print "\n\tKey :: $key" ; print "\n\tValue :: $value" ; }
Imagine my surprise then when the following was output -
Running /home/interface/.profile. Result set to 0 Key :: HOME Value :: /home/interface Key :: LOGNAME Value :: interface Key :: SHELL Value :: /usr/bin/sh Key :: TZ Value :: GB Key :: PATH Value :: /usr/bin:
Does anyone understand this? I can find no trace of any .profile to reflect the results in the ENV hash table! Cheers, Ronnie

Replies are listed 'Best First'.
Re: Perl/UNIX .profile problem
by Corion (Patriarch) on Nov 18, 2004 at 10:24 UTC

    The .profile is executed in a separate shell, which loads all the values, and then exits again, making all changes to the environment void. So you will need to employ something like tillys RE (tilly) 3: Get default login environment, which will import the environment set up by a shell script into the environment of the calling Perl process.

      I downloaded the code you suggested but there appears to be an error in it! When I attempt to test it I get the following error -
      syntax error at line 8, near "m/^(.*?)=((?:[^\n\\]|\\.| +\\\n)*)/gm ;"
      Cheers, Ronnie

        There's a closing ) missing on that line. Add it and the code will run... or see 408740.

        Cheerio, Sören

Re: Perl/UNIX .profile problem
by insensate (Hermit) on Nov 18, 2004 at 16:04 UTC
    I typically work around this in my environments (solaris) by using cron entries that look like this:
    00 00 * * * ksh -c '. /homes/myuser/.profile; /path/to/'
    (ksh is a personal preference)
Re: Perl/UNIX .profile problem
by Zaxo (Archbishop) on Nov 18, 2004 at 14:00 UTC

    The root problem is that cron jobs run with a restricted environment.

    The simplest solution is to edit interface's crontab, either setting SECTRAN_DIR = /home/interface/sectran at the top of the crontab file, or else in the command line which runs each script that needs it.

    After Compline,

      Actually it's not that cronjobs run in a restricted shell, the problem is that the OP is writing this settings to ~/.profile, which according to the docs:

      When Bash is invoked as an interactive login shell, or as a non-interactive shell with the `--login' option, it first reads and executes commands from the file `/etc/profile', if that file exists. After reading that file, it looks for `~/.bash_profile', `~/.bash_login', and `~/.profile', in that order, and reads and executes commands from the first one that exists and is readable.

      Pardon me for quoting the Bash docs, but this applies to any Bourne-type Shell.

      Translated to english: the shell that runs a cronjob is not an interactive shell, therefor your profile is not being read. There might be no shell at all (depending on the cron implementation). This is way your carefully crafted PATH sometimes doesn't work when calling things from a cronjob. You can either do what brother Zaxo says, or you can write a shell script and put all your settings there (PATH, SECTRAN_DIR, PERL5LIB, whatever).

      BTW, you can locally set an environment variable on the same command line when you run your script like this:

Re: Perl/UNIX .profile problem
by mce (Curate) on Nov 18, 2004 at 12:29 UTC
    If you really want to obfu it:
    $file = "/home/user/.profile"; grep(do{chop;s/(.*)=(.*)/$ENV{$1}=$2/e;},`. $file; env`);
    But I am sure that there are better solutions.

    Dr. Mark Ceulemans
    Senior Consultant
    BMC, Belgium
Re: Perl/UNIX .profile problem
by elwarren (Priest) on Nov 18, 2004 at 18:04 UTC
    Maybe I missed it, but why don't you set the $ENV{SECTRAN_DIR}='/some/dir' in your BEGIN block? Maybe die or populate it with a default if it isn't there?

Re: Perl/UNIX .profile problem
by meredith (Friar) on Nov 19, 2004 at 02:52 UTC

    I saw plenty of solutions, but nobody really explained the problem in the example to you. There are two key rules to environment variables in Unix-like systems:

    1. Each process has its own environment, and each process can only modify its own environment.
    2. When one process creates a child process, the environment of the parent process is copied into the new environment for the child.
    So, in your solution above, the profile script is executed in a child process. That is, the child gets copies of %ENV from your perl script (and the perl script got its %ENV from cron), proceeds to add/change the information as commanded in the script, and finally terminates. The changes to the child's environment go with it. Not a mark left on the world, poor kid. =)

    mhoward - at -
Re: Perl/UNIX .profile problem
by graff (Chancellor) on Nov 19, 2004 at 03:29 UTC
    Personally, I vote for insensate's solution (I ++'d a few other replies for intrinsic value, but he has the approach I would use).

    Note that in addition to the sub-shell versus perl environment issue cited by many folks above, there is yet another barrier that keeps your original script from working the way you want:

    Any statements within your script that modify %ENV will take effect at run-time. Meanwhile, perl will attempt to handle the "use sectran" directive at compile-time. Changes to %ENV within the script will come too late to have the intended effect, and this is another reason why insensate has the right approach, IMO.

    update: Seeing elwarren's remark about using the BEGIN block to set %ENV, I believe that even this won't get the %ENV settings done soon enough to help with "use". At least, when I tried something like this:

    BEGIN { $ENV{PERL5LIB} = "/some/special/lib" } use SpecialModule; # (a .pm file in /some/special/lib) ...
    it didn't work -- I got a report about "Can't locate". It would only work when I set PERL5LIB in the shell environment, and then ran the script in that shell (and then the BEGIN block is not needed, of course).
Re: Perl/UNIX .profile problem
by pelagic (Priest) on Nov 19, 2004 at 09:06 UTC
    Ronnie that's a pretty common problem you got and I see that you already posted a similar (Unix Aliases?) question here a while ago.
    I'd like to point you to solution that I already posted there a while ago.