如何在Perl脚本中引用一个shell脚本[环境变量]而不需要fork一个子shell?

8
我希望能够在不派生子shell的情况下从"my_perl.pl"中调用"env.sh"。我尝试使用反引号和system命令,像这样——>system (. env.sh)【点空格env.sh】,但是无法正常工作。
3个回答

14

子环境无法更改父环境。您最好的选择是从Perl代码内部解析env.sh,并在%ENV中设置变量:

#!/usr/bin/perl

use strict;
use warnings;

sub source {
    my $name = shift;

    open my $fh, "<", $name
        or die "could not open $name: $!";

    while (<$fh>) {
        chomp;
        my ($k, $v) = split /=/, $_, 2;
        $v =~ s/^(['"])(.*)\1/$2/; #' fix highlighter
        $v =~ s/\$([a-zA-Z]\w*)/$ENV{$1}/g;
        $v =~ s/`(.*?)`/`$1`/ge; #dangerous
        $ENV{$k} = $v;
    }
}

source "env.sh";

for my $k (qw/foo bar baz quux/) {
    print "$k => $ENV{$k}\n";
}

给定

foo=5
bar=10
baz="$foo$bar"
quux=`date +%Y%m%d`
它会打印输出
foo => 5
bar => 10
baz => 510
quux => 20110726

该代码只能处理简单的文件(例如,它不能处理if语句或foo=$(date))。如果您需要更复杂的东西,则编写一个用于源化env.sh的Perl脚本包装器是正确的做法(这也可能是一开始就正确的做法)。

在执行Perl脚本之前,另一个源化env.sh的原因是在Perl中设置环境变量可能发生得太晚,以至于期望看到它们的模块无法找到这些变量。

在文件foo中:

#!/bin/bash

source env.sh

exec foo.real

其中foo.real是您的Perl脚本。


2
您可以通过使用相关的shell来执行任意复杂的shell脚本,并将它们的环境转储到标准输出中,然后在同一进程中解析该输出。为了避免更改PATH等可能会在其他地方产生有趣副作用的内容,建议将输出馈送到除%ENV之外的其他东西或过滤特定感兴趣的值。我已经丢弃了派生shell脚本的标准输出和错误,尽管它们可以被重定向到临时文件并在perl脚本中用于诊断输出。
foo.pl:
#!/usr/bin/perl
open SOURCE, "bash -c '. foo.sh >& /dev/null; env'|" or
die "Can't fork: $!";

while(<SOURCE>) {
if (/^(BAR|BAZ)=(.*)/) {
    $ENV{$1} = ${2} ;
}
}

close SOURCE;

print $ENV{'BAR'} . "\n";

foo.sh: 导出 BAR=baz


只有当shell脚本有意地导出变量时,才能使用'env'。如果脚本没有明确导出它们,请尝试使用'set'而不是'env'。 - EdwinW

2

尝试以下UNIX代码示例:

cd /tmp

vi s

#!/bin/bash
export blah=test

vi t

#!/usr/bin/perl

if ($ARGV[0]) {
    print "ENV second call is : $ENV{blah}\n";
} else {
    print "ENV first call is : $ENV{blah}\n";
    exec(". /tmp/s; /tmp/t 1");
}

将"chmod 777 s t"翻译为中文是:修改s和t文件的权限为777。

执行"./t"命令。

"ENV first call is :"翻译为中文是:"第一次调用ENV的结果为:"。

"ENV second call is : test"翻译为中文是:"第二次调用ENV的结果为:test"。

技巧在于使用exec先执行你的bash脚本,然后再用一个参数调用你的perl脚本,这样你就知道自己被第二次调用了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接