Setting ENV varibles by calling a shell script within a perl program

A

ad

I need to make some system calls to other programs from a perl program
and those programs that are being called have some environment
variable dependencies that are set by a shell script program. So I
have to call this shell script to set up (export bunch of variables)
environment variables so that the other program that I call can run
fine.

If all possible, I am trying to avoid writing another shell program
that call the other shell program prior to my perl program since I
have lots of variable combination that my perl program can run with
and do not want to have all the variable validated with this extra
shell program so that it can call my perl program.

In the following example, I tried both "system" and "``" (backtricks)
but none of them worked.

---- Shell Program ------------
#!/bin/sh

export T1=test1
export T2=test2
export T3=test3
export T4=test4
---- End Of Shell Program -----

----- My Test Perl Program ----
#!/usr/bin/perl

use warnings;
use strict;

#system(". ./myShell.sh"); # Tried both
`. ./myShell.sh`;


print "T1 = $ENV{T1}\n"; # Prints blank
print "T2 = $ENV{T2}\n"; # Prints blank
print "T3 = $ENV{T3}\n"; # Prints blank
print "T4 = $ENV{T4}\n"; # Prints blank
----- End Of Perl Program ----

Thanks for the help and suggestions in advance.
 
T

Tad McClellan

ad said:
I need to make some system calls to other programs from a perl program
and those programs that are being called have some environment
variable dependencies that are set by a shell script program. So I
have to call this shell script to set up (export bunch of variables) ^^^^^^^
environment variables so that the other program that I call can run
fine.


No you don't.

You can set env vars in Perl via the %ENV hash.

Any external processes (child) you start will inherit their env from
the Perl program (parent).

If all possible, I am trying to avoid writing another shell program
that call the other shell program prior to my perl program since I
have lots of variable combination that my perl program can run with
and do not want to have all the variable validated with this extra
shell program so that it can call my perl program.


If you want access to those env vars in your Perl program, then
wrapping it in a sh program is what you should do.

I don't think I understand what your objection to that approach is.

Why is that not a good solution?

#system(". ./myShell.sh"); # Tried both


If you just want to launch programs that make use of those env vars,
and don't need them in your Perl program, then just set them in
the same (sub) process:


system '. ./myShell.sh; other_prog';
 
D

Darin McBride

Tad said:
No you don't.

Sometimes ... you do.

e.g., I have a program "P" which can be installed anywhere. There is a
script which may call a script which may call a script that sets
"P_HOME". That script, or a symlink to it, is in a well-known place,
even if that is relative to where my perl script is located.

Much easier, safer, and maintainable would be to have my perl script
use the shell to interpret the script that sets the environment. This
way, when the sysadmin moves P to a new location, s/he only must
maintain a single point of contact (the shell script), which s/he is
very likely to remember to do, but would be very unlikely to remember
to update a perl script to have the same ENV entry.
You can set env vars in Perl via the %ENV hash.

Yes - but that's not always useful.
Any external processes (child) you start will inherit their env from
the Perl program (parent).




If you want access to those env vars in your Perl program, then
wrapping it in a sh program is what you should do.

I don't think I understand what your objection to that approach is.

Why is that not a good solution?

An extra layer of dependancy? In this world, we have A.sh calling
A.pl. We then need to ensure that no one calls A.pl except through
A.sh. This may be trivial for a new program, but if it's an existing
perl script which is being called by numerous automated processes, that
may not be as good.
If you just want to launch programs that make use of those env vars,
and don't need them in your Perl program, then just set them in
the same (sub) process:


system '. ./myShell.sh; other_prog';

Yes - I think this is the easiest solution that completely fulfills the
OPs requests. The downside is that if "other_prog" has shell
metacharacters in the commandline, or anything else the shell may take
funny, it'll be a blast getting around it.


Another solution, which is kinda ugly, is to do something like this:

my @lines = `. myShell.sh; env`;

Then, parse each line, splitting on '=', and putting them into your
%ENV hash:

foreach my $line (@lines)
{
/([^=]+)=(.*)/ and $ENV{$1} = $2;
}

This may have some funny side effects - if myShell.sh sets some
variable that Perl ends up using, what happens depends on whether the
perl module in question has already read that environment variable or
not.

Here, then, you can go back to using the multi-arg version of system to
avoid the shell's reinterpretation of your commands.
 
S

Sam Holden

[snip]
If you want access to those env vars in your Perl program, then
wrapping it in a sh program is what you should do.

I don't think I understand what your objection to that approach is.

Why is that not a good solution?

An extra layer of dependancy? In this world, we have A.sh calling
A.pl. We then need to ensure that no one calls A.pl except through
A.sh. This may be trivial for a new program, but if it's an existing
perl script which is being called by numerous automated processes, that
may not be as good.

I'd do

; mv perl_program ORIGperl_program
; cat said:
#!/bin/sh
. myShell.sh
exec ORIGperl_program
HERE
; chmod 755 perl_program

Probably using full paths...

Unless there are some situations where MyShell.sh should *not* be
sourced before executing the perl program or sourcing MyShell.sh
twice should *not* be done then I can't see a problem with that.
All the automated processes are going to now be calling the shell
script wrapper.

If either of those two situations apply then I'd consider that a huge
bug that needs to be fixed anyway.

Or modify myShell.sh to also do:

MYSHELL_HAS_BEEN_RUN=1
EXPORT MYSHELL_HAS_BEEN_RUN

and modify the perl program to start with:

[completely untested, I haven't considered handling the various
ways a program can be started (and effect on $0)]

use strict;
use warnings;
use FindBin;
use File::Basename;

unless ($ENV{MYSHELL_HAS_BEEN_RUN}) {
exec ". myShell.sh; $FindBin::Bin" . basename($0);
}

But that seems error prone.
 
A

ad

Thanks for all the replies and information. The above system call helped
to solve my problem. I didn't think about calling multiple calls in one
"system" before.

Thanks everyone again.
 
U

Uri Guttman

DM> Another solution, which is kinda ugly, is to do something like this:

DM> my @lines = `. myShell.sh; env`;

that is the best way to do it. it isn't ugly, just clumsy.

DM> Then, parse each line, splitting on '=', and putting them into your
DM> %ENV hash:

DM> foreach my $line (@lines)
DM> {
DM> /([^=]+)=(.*)/ and $ENV{$1} = $2;
DM> }

bah.

my $env = `. myShell.sh; env`;

local( %ENV ) = $env =~ m/^([^=]+)=(.+)$/mg ;

DM> This may have some funny side effects - if myShell.sh sets some
DM> variable that Perl ends up using, what happens depends on whether the
DM> perl module in question has already read that environment variable or
DM> not.

not if you localize %ENV first. do it in a block and the old %ENV be
back when you exit the block.

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,818
Latest member
Brigette36

Latest Threads

Top