我有一个在Windows上运行的perl代码。此代码调用open
两次以执行两个不同的批处理文件。第一个设置一个环境变量,第二个需要使用它。遗憾的是,设置为变量的值在两次调用之间丢失。
这是我的Perl代码。
my $hdl;
open($hdl, "set.bat |");
while(my $line = <$hdl>) {
print("$linen");
}
close($hdl);
open($hdl, "get.bat |");
while(my $line = <$hdl>) {
print("$linen");
}
close($hdl);
我的set.bat
设置环境变量的文件:
set VAR=20
echo %VAR%
还有我的get.bat
使用环境变量:
echo %VAR%
当我运行perl代码时,结果如下:
>perl my_code.pl
>set VAR=20
>echo 20
20
>echo
ECHO is on.
我们可以看到set.bat
正确设置了VAR
的值,但get.bat
无法使用它。
此外,如果我从cmd
提示符连续运行两个批处理脚本,我会得到我期望的结果:
>set.bat
>set VAR=20
>echo 20
20
>get.bat
>echo 20
20
我该怎么做才能让我的第二个批处理脚本在我的 perl 代码中使用我的环境变量?
带有管道的open
创建一个新进程†然后您的set.bat
在该进程中运行并在该进程中设置该环境变量,然后退出。
然后get.bat
在一个不同的进程中运行,看不到第一个进程。
但是,它们都继承了运行 Perl 脚本的进程的环境。因此,您可以在脚本中设置所需的环境(例如通过%ENV
(,然后创建一个子流程,然后该子流程查看环境。‡
另一方面,您可以在同一子进程中运行两个 shell 脚本,如果这符合您的目的,例如通过system
。然后可以将变量export
到环境中,在它source
-ed 之后,下一个要运行的脚本将看到它。这是Linux中的一个例子(现在不能做Windows(。
命令行程序("单行"(
perl -we'system("/bin/bash", "-c", q(source set.bat.sh; get.bat.sh))'
与文件set.bat.sh
#!/bin/bash
VAR=20
export VAR
和文件get.bat.sh
#!/bin/bash
echo $VAR
单行打印一行带有20
.
†管道open
通常被称为fork
进程(参见打开(,但在Windows上只能模拟(通过线程(,因为没有本机fork
;参见perlfork。但是,perlport 表示在 Windows 上 管道 -open
确实创建了一个子进程(通过 Win32 API(。
‡一个Linux示例(目前无法在Windows中测试(
perl -we'$v = qx("set.bat.sh"); chomp $v; $ENV{VAR} = $v; system("get.bat.sh")'
与set.bat.sh
文件
#!/bin/bash
VAR=20
echo $VAR
和get.bat.sh
#!/bin/bash
echo $VAR
由于子进程不能直接更改其父进程的环境,因此set.bat.sh
$VAR
打印到STDOUT
,然后其父进程perl脚本可以从该流(由qx
捕获(中读取它并将其设置在自己的环境中,其下一个(孙(子get.bat.sh
继承。 (一个system
分叉一个进程,其中另一个进程被分叉以运行get.bat.sh
。环境是向下传递的。
当然,这样做的弱点是perl脚本需要知道变量的名称,VAR
。 然后,改进是让set.bat
将名称本身与值一起发送。
这并不容易做到。 原因是,子进程通常继承父进程环境的副本。包括环境变量。
请参阅:如何在Perl脚本之间共享ENV变量
子进程彼此隔离,通常不能影响彼此或父进程的环境。
对于这个有限的示例,也许在Perl中重新实现部分CMD语言就足够了。如果你只需要支持一个小的子集,比如说,将静态字符串分配给变量,这并不难:
if ($line ~ /^sets+(w+)=(w+)$/) {
$ENV{$1} = $2;
}
如果您不是绝对需要将这些值传播到环境中,则可以使用与ENV
不同的哈希。