Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Perl equiv for ksh source ('.' dot) command

59 views
Skip to first unread message

Dave Sutter

unread,
Mar 9, 2000, 3:00:00 AM3/9/00
to
I need to source in an existing Korn Shell script from my perl script.
The purpose of this ksh script to set a bunch of environment variables
that will be used by applications that I will be triggering from my
perl script. Thus, I cannot just run the ksh script in a sub-shell, I
need to "source" it so that the environment variable settings take
effect in the current environment of my perl script. I do not need
these changes to affect the environment of the shell from which my perl
script was called, or anything wacky like that.

In a ksh script this is very easily done with the "." (dot)
command ". ./env_script". So far, the only way that I have been able
to accomplish this in my perl script is with the following very ugly
code:

open(NEW_ENV, ". ./env_script; env |") or die "Cannot run . or set";
while(<NEW_ENV>) {
chomp;
($name, $value) = split(/=/, $_, 2);
if ($ENV{"$name"} ne "$value") {
$ENV{"$name"} = "$value";
}
}

Anyone have a better way to do this?

Better efficiency and fewer lines of code would be nice, but are not my
main concern here. I'm mostly interested in making it more readable.
It should simply be more obvious what I'm trying to accomplish here by
looking at the code. For example, I know that I could drop the if
statement entirely... but that might make the purpose less obvious
(although, at this point, it's already so obfuscated...)

I've tinkered with lots of ideas involving eval() or system() or
backticks or BEGIN {} or the ENV module or other such trickery, all to
no avail. Any suggestions are appreciated.

--
Dave Sutter (dsu...@my-deja.com)


Sent via Deja.com http://www.deja.com/
Before you buy.

Larry Rosler

unread,
Mar 9, 2000, 3:00:00 AM3/9/00
to
In article <8a8r9a$tav$1...@nnrp1.deja.com> on Thu, 09 Mar 2000 18:41:47
GMT, Dave Sutter <dsu...@my-deja.com> says...

...

> open(NEW_ENV, ". ./env_script; env |") or die "Cannot run . or set";

Include $! in the diagnostic. Or you can read the output of the command
directly. See below.

> while(<NEW_ENV>) {
> chomp;
> ($name, $value) = split(/=/, $_, 2);
> if ($ENV{"$name"} ne "$value") {
> $ENV{"$name"} = "$value";
> }
> }
>
> Anyone have a better way to do this?

Slightly. See below for an equivalent one-liner.



> Better efficiency and fewer lines of code would be nice, but are not my
> main concern here. I'm mostly interested in making it more readable.
> It should simply be more obvious what I'm trying to accomplish here by
> looking at the code.

How about using a one-line comment?

> For example, I know that I could drop the if
> statement entirely... but that might make the purpose less obvious
> (although, at this point, it's already so obfuscated...)

Hardly. All it does is slow the thing down unnecessarily. It is far
cheaper just to set the variable and be done with it. And double-
quoting simple strings is misleading and unnecessary.

> I've tinkered with lots of ideas involving eval() or system() or
> backticks or BEGIN {} or the ENV module or other such trickery, all to
> no avail. Any suggestions are appreciated.

# Add new values to the environment and transfer them to %ENV.
open NEW_ENV, '. ./env_script; env |' or die "Cannot get env. $!\n";
/([^=]+)=(.*)/ and $ENV{$1} = $2 while <NEW_ENV>;

or

# Add new values to the environment and transfer them to %ENV.
/([^=]+)=(.*)/ and $ENV{$1} = $2 for `. ./env_script; env`;

I'm not sure if that meets your goal of readability, though. To me, it
reads just fine, but YMMV :-).

--
(Just Another Larry) Rosler
Hewlett-Packard Laboratories
http://www.hpl.hp.com/personal/Larry_Rosler/
l...@hpl.hp.com

Sam Holden

unread,
Mar 10, 2000, 3:00:00 AM3/10/00
to
On Thu, 9 Mar 2000 11:48:24 -0800, Larry Rosler <l...@hpl.hp.com> wrote:

<snip someone elses longer way of achieving same>

>
> # Add new values to the environment and transfer them to %ENV.
> open NEW_ENV, '. ./env_script; env |' or die "Cannot get env. $!\n";
> /([^=]+)=(.*)/ and $ENV{$1} = $2 while <NEW_ENV>;
>
>or
>
> # Add new values to the environment and transfer them to %ENV.
> /([^=]+)=(.*)/ and $ENV{$1} = $2 for `. ./env_script; env`;

Or (assuming the env command will output all existing env vars and nothing
else):

%ENV = map {split /=/,$_,2} split /\n/, `. ./env_script; env`;

--
Sam

Perl was designed to be a mess (though in the nicest of possible ways).
--Larry Wall

Villy Kruse

unread,
Mar 13, 2000, 3:00:00 AM3/13/00
to
On Thu, 09 Mar 2000 18:41:47 GMT, Dave Sutter <dsu...@my-deja.com> wrote:

>
>open(NEW_ENV, ". ./env_script; env |") or die "Cannot run . or set";

>while(<NEW_ENV>) {
> chomp;
> ($name, $value) = split(/=/, $_, 2);
> if ($ENV{"$name"} ne "$value") {
> $ENV{"$name"} = "$value";
> }
>}
>

How would that parse the line:

ABC=1 DEF=2 GHI=3

from ./env_script?

Just something to think about.


Villy

Dave Sutter

unread,
Mar 13, 2000, 3:00:00 AM3/13/00
to
In article <slrn8cggvr...@pgrad.cs.usyd.edu.au>,

sho...@cs.usyd.edu.au wrote:
> Or (assuming the env command will output all existing env vars and
> nothing else):
>
> %ENV = map {split /=/,$_,2} split /\n/, `. ./env_script; env`;

That code replaces my code nicely, and it taught me a thing or two
about the map function; thanks. It looks like I may just have to go
with Larry Rosler's idea of putting a comment in to explain the code,
and get over my desire to make the code more obvious.

However, I wasn't really looking to improve my existing method (though
that is appreciated), I was really looking for a different method
altogether. I was hoping that there was some way to do this without
having to parse the output of the env (or set) command and stuff it
into the %ENV hash. To me, that was the ugly, non-obvious part (it
assumes that you are using ksh to invoke your perl script, among other
problems).

Perhaps my original question was not clear. Let me try again:

Anyone know of a perl way to execute a shell script in the current
environment so that the script's environment variable settings affect
the current environment (similar to the ksh "." command, or the
csh "source" command )?

Larry Rosler

unread,
Mar 13, 2000, 3:00:00 AM3/13/00
to
In article <8ajd1u$6v3$1...@nnrp1.deja.com> on Mon, 13 Mar 2000 18:46:23
GMT, Dave Sutter <dsu...@my-deja.com> says...

...

> Perhaps my original question was not clear. Let me try again:


>
> Anyone know of a perl way to execute a shell script in the current
> environment so that the script's environment variable settings affect
> the current environment (similar to the ksh "." command, or the
> csh "source" command )?

Neither I nor the FAQ know of a way, though I'd be delighted to learn
one.


perlfaq8: "I {changed directory, modified my environment} in a perl
script. How come the change disappeared when I exited the script? How do
I get my changes to be visible?"

Unix
In the strictest sense, it can't be done -- the script executes as a
different process from the shell it was started from. Changes to a
process are not reflected in its parent, only in its own children
created after the change. There is shell magic that may allow you to
fake it by eval()ing the script's output in your shell; check out the
comp.unix.questions FAQ for details.

In your example, the capture of the 'env' command's output does the same
thing at the same level as the suggested eval at the next higher level.

Jonathan Stowe

unread,
Mar 13, 2000, 3:00:00 AM3/13/00
to
On Mon, 13 Mar 2000 18:46:23 GMT Dave Sutter wrote:
>
> Anyone know of a perl way to execute a shell script in the current
> environment so that the script's environment variable settings affect
> the current environment (similar to the ksh "." command, or the
> csh "source" command )?
>

But what you have been shown is what the shell does - in its own way of
course. The question you are asking is in the FAQ - of comp.unix.programmer.
For a Perl program to execute a shell script it must spawn a new process -
after the completion of which any environment it has created will be gone.
The shell has the liberty to interpret a 'shell script' itself and thus
retain any changes to the environment - that is what the '.' does. If
however you have some program run in the dotted in file that changes the
environment then that environment will be lost on its completion.

/J\
--
Jonathan Stowe
http://www.gellyfish.com
http://www.tackleway.co.uk

David Kernen

unread,
Mar 21, 2000, 3:00:00 AM3/21/00
to
> On Mon, 13 Mar 2000 18:46:23 GMT Dave Sutter wrote:
> >
> > Anyone know of a perl way to execute a shell script in the current
> > environment so that the script's environment variable settings affect
> > the current environment (similar to the ksh "." command, or the
> > csh "source" command )?
> >

Of hand I can think of three approaches you could use, each of which has its
strengths and weaknesses.
The first would be to not try to do anything with the shell script from within
perl. Instead, source it
(via the '.' command or source command, for example) from within another script,
which then calls your
perl program. The perl program will then inherit the environment of its caller,
which has already been
altered by your sourced script. This disadvantage is that you have to run the
perl script from within another script,
and additionally you have to know what shell script you want to run before you
call your perl script, which is sometimes
a no-brainer but at other times is something you want your perl script to figure
out for you.

A second approach that works well if the shell script is just a bunch of
VAR=value lines is to just parse the shell script
as if it were a config file of key=value pairs. This doesn't work though if the
shell script has logic in it.

A third approach would be to fork off a shell, run the shell script, and then run
the "set" command to print out the
environment. Have your perl script capture the output from set and process it,
either by (re)setting its own environmental
variables or by setting like-named global vars or stuffing a hash or something. I
once had to write a script that could read
in a script provided by a software product in order to perform automated
processing, and the software product's script
(written in ksh) would change from time to time. In order to avoid having to
mainain the perl script, I did something like
this (this is from memory so don't expect perl -cw to be quiet!):

use vars qw(%ENV);
my $script='/opt/product/bin/product.envars.sh';
my @list = `/bin/sh '. $script ; set '`; # run the script and the "set"
command; place output in @list
foreach my $x (@list) {
chomp $x;
# parse output from 'set' into key/value pairs:
my($k,$v) = $x =~
/^ # anchor at beginning
([^=]+) # find all non-equal sign chars (for $k)
= # then the equal sign
(.*) # then $v
/x;
$ENV{"$k"} = $v;
}

This approach worked pretty well for me but you need to be careful if you have
variables with embedded newlines or something
funky like that.

Hope this helps...
Dave Kernen


Villy Kruse

unread,
Mar 31, 2000, 3:00:00 AM3/31/00
to
On Thu, 30 Mar 2000 23:02:28 GMT, Dave Sutter <dsu...@my-deja.com> wrote:

>
>That issue is not applicable here. I want to change the "current"
>environment (the environment that the perl sript is in) not the
>environment of the shell that called my perl script (which is what that
>FAQ is about). I want the equivalent of the ksh "." command or
>csh "source" command, not anything more elaborate.
>

Anyone know if the old dotsh.pl is any good?

$ more /usr/lib/perl5/dotsh.pl
::::::::::::::
/usr/lib/perl5/dotsh.pl
::::::::::::::
#
# @(#)dotsh.pl 03/19/94
#
# Author: Charles Collins
#
# Description:
# This routine takes a shell script and 'dots' it into the current perl
# environment. This makes it possible to use existing system scripts
# to alter environment variables on the fly.
#
# Usage:
# &dotsh ('ShellScript', 'DependentVariable(s)');
#
# where
#
# 'ShellScript' is the full name of the shell script to be dotted
#
# 'DependentVariable(s)' is an optional list of shell variables in the
# form VARIABLE=VALUE,VARIABLE=VALUE,... that 'ShellScript' is
# dependent upon. These variables MUST be defined using shell syntax.
#
# Example:
# &dotsh ('/tmp/foo', 'arg1');
# &dotsh ('/tmp/foo');
# &dotsh ('/tmp/foo arg1 ... argN');
#

--
Villy

0 new messages