如何从perl脚本运行“source”命令(Linux)?

np8igboo  于 2022-11-15  发布在  Perl
关注(0)|答案(3)|浏览(468)

我正在尝试从Perl脚本(script.pl)中source一个脚本。

system ("source /some/generic/script");

请注意,这个通用脚本可以是shell、python或任何其他脚本。而且,我无法将这个通用脚本中的逻辑复制到我的Perl脚本中。我尝试用′、execqx//替换system。每次我都得到以下错误:
无法执行“source”:www.example.com第18行没有这样的文件或目录script.pl。
我在网上看到很多论坛,讨论了这个问题的各种原因。但是没有一个提供解决方案。有没有办法从Perl脚本运行/执行source命令?

im9ewurl

im9ewurl1#

在bash等中,source是一个内置的,意思是 * 读取这个文件,并在本地解释它 *(有点像#include)。
在这种情况下,这样做毫无意义--您要么需要从命令中删除source,并在shell脚本的开头添加一个shebang(#!)行,告诉系统使用哪个shell来执行该脚本,要么需要显式地告诉system使用哪个shell,例如:

system "/bin/sh", "/some/generic/script";

[with没有关于在这种情况下使用system实际上是否合适的注解]。

nkoocmlb

nkoocmlb2#

这里有几个问题。首先,子进程不能改变其父进程的环境。source只会在它的进程存在时存在。
下面是一个设置和导出环境变量的简短程序。

#!/bin/sh
echo "PID" $$
export HERE_I_AM="JH";

运行文件不会导出变量。文件在其自己的进程中运行。进程ID($$)在 set_stuff.sh 和shell中是不同的:

$ chmod 755 set_stuff.sh
$ ./set_stuff.sh
PID 92799
$ echo $$
92077    
$ echo $HERE_I_AM       # empty

source则不同。它读取文件并在shell中对其进行求值。进程ID在 set_stuff.sh 和shell中是相同的,因此文件实际上影响了它自己的进程:

$ 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命令的一部分传递

$ 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而不是解析它。

z9gpfhce

z9gpfhce3#

你不能。source是一个shell函数,它将脚本的内容“导入”到你当前的环境中。它不是一个可执行文件。
您可以通过滚动您自己的功能来复制它的一些功能-运行或解析您正在“获取”的任何内容并捕获结果:

print `. file_to_source; echo $somevar`;

、或类似物。

相关问题