【问题标题】:How to run "source" command (Linux) from a perl script?如何从 perl 脚本运行“源”命令(Linux)?
【发布时间】:2016-02-20 06:39:09
【问题描述】:

我正在尝试 source 来自 Perl 脚本 (script.pl) 的脚本。

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

请注意,此通用脚本可以是 shell、python 或任何其他脚本。此外,我无法将此通用脚本中存在的逻辑复制到我的 Perl 脚本中。我尝试将system 替换为``、execqx//。每次我收到以下错误:

无法执行“source”:在 script.pl 第 18 行没有这样的文件或目录。

我在互联网上遇到了许多论坛,它们讨论了这个问题的各种原因。但他们都没有提供解决方案。有没有办法从 Perl 脚本运行/执行 source 命令?

【问题讨论】:

    标签: bash perl


    【解决方案1】:

    在 bash 等中,source 是一个内置函数,表示读取此文件,并在本地解释它(有点像#include)。

    在这种情况下,这是没有意义的 - 你要么需要从命令中删除 source 并在 shell 脚本的开头有一个 shebang (#!) 行,告诉系统使用哪个 shell 来执行它脚本,或者你需要明确告诉system 使用哪个shell,例如

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

    [没有评论在这种情况下使用system是否真的合适]。

    【讨论】:

    • 如果我不知道(不确定)它是哪种(shell 或其他)脚本怎么办?就我而言,要调用的脚本在文件开头没有 shebang (#!) 行。
    • 在这种情况下它需要一个。恕我直言,每个解释的脚本都应该有一个。
    • 如果它没有shebang,你必须假设它是打算用/bin/sh执行的。
    • @chepner that 和之前对source 的使用也是一个强有力的指标,但是将 shebang 放入 比猜测(并且可能猜测)更好错了,覆盖了shebang)
    • @Anand,要么你需要知道它是为哪个 shell 编写的(这样你就可以指定使用哪个 shell),要么它需要有 shebang(这样可以直接执行)。没有办法解决它。
    【解决方案2】:

    这里发生了一些事情。首先,子进程不能改变其父进程的环境。 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 而不是解析它。

    【讨论】:

      【解决方案3】:

      你不能。 source 是一个 shell 函数,可以将该脚本的内容“导入”到您当前的环境中。它不是可执行文件。

      您可以通过自己滚动复制其中的一些功能 - 运行或解析您“采购”的任何内容并捕获结果:

       print `. file_to_source; echo $somevar`;
      

      或类似的。

      【讨论】:

        猜你喜欢
        • 2013-07-18
        • 1970-01-01
        • 2019-07-04
        • 2012-10-01
        • 2014-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-20
        相关资源
        最近更新 更多