这里发生了一些事情。首先,子进程不能改变其父进程的环境。 source 只会在其进程存在时持续。
这是一个设置和导出环境变量的简短程序。
#!/bin/sh
echo "PID" $$
export HERE_I_AM="JH";
运行文件不会导出变量。该文件在其自己的进程中运行。 set_stuff.sh 和 shell 中的进程 ID ($$) 不同:
$ chmod 755 set_stuff.sh
$ ./set_stuff.sh
PID 92799
$ echo $$
92077
$ echo $HERE_I_AM # empty
source 不同。它读取文件并在 shell 中对其进行评估。 set_stuff.sh 和 shell 中的进程 ID 相同,因此文件实际上是在影响自己的进程:
$ unset HERE_I_AM # start over
$ source set_stuff.sh
PID 92077
$ echo $$
92077
$ echo $HERE_I_AM
JH
现在到 Perl。调用 system 会创建一个子进程(在某处有一个 exec),因此不会影响 Perl 进程。
$ perl -lwe 'system( "source set_stuff.sh; echo \$HERE_I_AM" );
print "From Perl ($$): $ENV{HERE_I_AM}"'
PID 92989
JH
Use of uninitialized value in concatenation (.) or string at -e line 1.
From Perl (92988):
奇怪的是,即使您的版本没有,这也有效。我认为不同的是,这里没有特殊的 shell 元字符,所以它尝试执行程序目录,跳过它刚刚用于我更复杂的字符串的 shell:
$ perl -lwe 'system( "source set_stuff.sh" ); print $ENV{HERE_I_AM}'
Can't exec "source": No such file or directory at -e line 1.
Use of uninitialized value in print at -e line 1.
但是,在这种情况下,您不需要单个字符串。列表形式更安全,但source 不是任何东西都可以执行的文件:
$ which source # nothing
$ perl -lwe 'system( "source", "set_stuff.sh" ); print "From Perl ($$): $ENV{HERE_I_AM}"'
Can't exec "source": No such file or directory at -e line 1.
Use of uninitialized value in concatenation (.) or string at -e line 1.
From Perl (93766):
也就是说,您可以调用 source,但作为调用 shell 的东西。
回到你的问题。有多种方法可以解决这个问题,但我们需要获取程序的输出。而不是system,使用反引号。这是一个双引号上下文,所以我需要保护一些我想作为 shell commans 的一部分传递的文字 $s
$ perl -lwe 'my $o = `echo \$\$ && source set_stuff.sh && echo \$HERE_I_AM`; print "$o\nFrom Perl ($$): $ENV{HERE_I_AM}"'
Use of uninitialized value in concatenation (.) or string at -e line 1.
93919
From Shell PID 93919
JH
From Perl (93918):
在反引号内,你会得到你喜欢的。 shell 程序可以看到这个变量。一旦回到 Perl,它就不能了。但是,我现在有输出。让我们变得更花哨。摆脱 PID 的东西,因为我现在不需要看到它:
#!/bin/sh
export HERE_I_AM="JH";
shell 命令会创建一些具有名称和值的输出:
$ perl -lwe 'my $o = `source set_stuff.sh && echo HERE_I_AM=\$HERE_I_AM`; print $o'
HERE_I_AM=JH
我可以解析该输出并在 Perl 中设置变量。现在Perl已经导入了shell程序的部分环境:
$ perl -lwe 'my $o = `source set_stuff.sh && echo HERE_I_AM=\$HERE_I_AM`; for(split/\R/,$o){ my($k,$v)=split/=/; $ENV{$k}=$v }; print "From Perl: $ENV{HERE_I_AM}"'
From Perl: JH
不过,让我们获取整个环境。 env 以我刚刚处理的方式输出每个值:
$ perl -lwe 'my $o = `source set_stuff.sh && env | sort`; print $o'
...
DISPLAY=:0
EC2_PATH=/usr/local/ec2/ec2-api-tools
EDITOR=/usr/bin/vi
...
我在 shell 中设置了几百个变量,我不想暴露其中的大部分。这些都是Perl进程设置的,所以我可以暂时清除%ENV:
$ perl -lwe 'local %ENV=(); my $o = `source set_stuff.sh && env | sort`; print $o'
HERE_I_AM=JH
PWD=/Users/brian/Desktop/test
SHLVL=1
_=/usr/bin/env
将其与后处理代码放在一起,您就可以将该信息传回给父级。
顺便说一句,这类似于将变量传回父 shell 进程的方式。由于该输出已经是 shell 理解的内容,因此您使用 shell's eval 而不是解析它。