Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I keep my environment variables between two calls to open?

I have a perl code running on Windows. This code calls open twice to execute two different batch files. The first one set an environment variable and the second one needs to use it. Unfortunately, the value sets to the variable is lost between the two calls.

Here is my Perl code.

my $hdl;
open($hdl, "set.bat |");
while(my $line = <$hdl>) {
    print("$line\n");
}
close($hdl);

open($hdl, "get.bat |");
while(my $line = <$hdl>) {
    print("$line\n");
}
close($hdl);

My set.bat file which sets the environment variable:

set VAR=20
echo %VAR%

And my get.bat which uses the environment variable:

echo %VAR%

When I run the perl code, here is the results:

>perl my_code.pl
>set VAR=20
>echo 20
20
>echo
ECHO is on.

We can see set.bat correctly sets the value of VAR but get.bat is unable to use it.


Also, if I run my two batch scripts in a row from the cmd prompt, I have the result I expect:

>set.bat
>set VAR=20
>echo 20
20
>get.bat
>echo 20
20

What can I do to let my second batch script to use my environment variable in my perl code?

like image 937
Pierre Avatar asked Dec 22 '22 19:12

Pierre


2 Answers

The open with a pipe creates a new process and then your set.bat runs in that process and sets that environment variable in that process, which then exits.

Then get.bat runs in a yet different process and can see nothing of the first process.

However, they both inherit the environment of the process in which your Perl script is running. So you can set the needed environment in the script (via %ENVfor instance) and then create a subprocess, and then that subprocess sees the environment.

On the other hand, you can run both shell scripts in the same subprocess, if that fits your purposes, via system for instance. Then one can export a variable into the environment, and after it's source-ed the next script to run will see it. Here is an example in Linux (can't do Windows right now).

A command-line program ("one-liner")

perl -we'system("/bin/bash", "-c", q(source set.bat.sh; get.bat.sh))'

with the file set.bat.sh

#!/bin/bash
VAR=20
export VAR

and file get.bat.sh

#!/bin/bash
echo $VAR

The one-liner prints one line with 20.


The pipe-open is normally said to fork a process (see open), but on Windows that can only be emulated (via threads) as there is no native fork; see perlfork. However, perlport indicates that on Windows the pipe-open does create a subprocess (via Win32 API).


A Linux example (can't test in Windows right now)

perl -we'$v = qx("set.bat.sh"); chomp $v; $ENV{VAR} = $v; system("get.bat.sh")'

with set.bat.sh file

#!/bin/bash
VAR=20
echo $VAR

and get.bat.sh

#!/bin/bash
echo $VAR

Since a subprocess cannot directly change the environment of its parent the set.bat.sh prints $VAR to STDOUT and then its parent, the perl script, can read it from that stream (captured by qx) and set it in its own environment, which its next (grand-)child get.bat.sh inherits. (A system forks a process, and in it another one is forked to run get.bat.sh. Environment is passed down.)

The weakness of this, of course, is that the perl script needs to know the name of the variable, VAR. An improvement then would be for the set.bat to send the name itself along with the value.

like image 123
zdim Avatar answered Jan 05 '23 00:01

zdim


That's not easily done. The reason is, that a child process normally inherits a copy of the parents process environment. Including environment variables.

See: How to Share ENV Variables Among Perl Scripts

like image 31
Daniel Heinrich Avatar answered Jan 05 '23 01:01

Daniel Heinrich