能否在Perl脚本中引用一个Shell脚本?

5

我们是否可以在perl脚本中调用shell脚本?

示例:

cat test1.sh
#!/bin/ksh
DATE=/bin/date

程序2:

cat test2.sh
#!/bin/ksh
. ./test1.sh  
echo `$DATE`

程序3:

cat test3.pl
#!/usr/bin/perl
### here I need to source the test1.sh script
print `$DATE`'

如何在perl中调用shell以便在执行test3.pl时打印日期

谢谢 raghu


你不想使用Perl的日期时间模块吗?有什么原因呢? - ghostdog74
1
@ghostdog74,你甚至不需要一个模块。在标量上下文中,localtime函数会产生一个几乎相同的字符串:perl -le 'print scalar localtime',你只需要获取时区,它们就会完全一致。 - Chas. Owens
嘿,日期只是一个例子...它可以是任何东西代替日期(如ls、chown、echo......)无论第一个文件中的命令是什么,我都需要将其源到Perl中; - raghu
7个回答

4

您无法执行

system("source src.sh");

system()会启动一个新的子shell,您的环境变量不会传递到运行Perl脚本的shell中。即使您的shell脚本导出了变量,它也只会将它们导出到子shell而不是实际的shell。

一种解决方法是编写一个包装脚本,该脚本:

  1. 首先调用shell脚本,然后
  2. 运行Perl脚本

1
这通常是我所做的,但如果您想根据您所做的工作来源不同的文件,则无法正常工作。例如,假设您有三个数据库并需要不同的环境来访问它们。您事先不知道需要连接哪个数据库。在这种情况下,包装器方法会失效。 - Chas. Owens

2

是的,现在您可以使用Env::Modify模块来实现此功能。

use Env::Modify qw(:ksh source);
source("./test1.sh");     # env settings from test1.sh now available in Perl
print `$ENV{DATE}`;       # print `/bin/date`;

1

我不知道这是否有帮助,但我感到有必要想出一种用Perl编写此代码的方法。我的意图是让Perl运行一个shell脚本,并将其设置的任何shell变量分配给Perl脚本中同名的变量。

其他人说得对,你“source”任何shell脚本都会在子shell中。我想我可以使用“sh -x cmd”至少让shell显示设置的变量。

这是我写的代码:

use strict;  use warnings;

our $DATE;

my $sh_script = "./test1.sh";

my $fh;
open($fh, "sh -x '$sh_script' 2>&1 1>/dev/null |") or die "open: $!";
foreach my $line (<$fh>) {
    my ($name, $val);
    if ($line =~ /^\+ (\w+)='(.+)'$/) {  # Parse "+ DATE='/bin/date;'
        $name = $1;
        ($val = $2) =~ s{'\\''}{'}g;  # handle escaped single-quotes (eg. "+ VAR='one'\''two'")
    } elsif ($line =~ /^\+ (\w+)=(\S+)$/) {  # Parse "+ DATE=/bin/date"
        $name = $1;
        $val = $2;
    } else {
        next;
    }
    print "Setting '$name' to '$val'\n" if (1);
    # NOTE: It'd be better to use something like "$shell_vars{$name} = $val",
    #  but this does what was asked (ie. $DATE = "/bin/date")...
    no strict 'refs';
    ${$name} = $val;    # assign to like-named variable in Perl
}
close($fh) or die "close: ", $! ? $! : "Exit status $?";

print "DATE: ", `$DATE` if defined($DATE);

当然,你可以做更多的错误检查,但如果你只想捕获shell变量,那么这对我来说就足够了。


1

您可以做一些简单的事情,比如这样:

system "source /path/to/shell_env.sh &&"
     . "/path/to/script.sh";

请注意,这与以下不推荐的内容不同:

   system "source /path/to/shell_env.sh &&"
        . "/bin/sh /path/to/script.sh";

1
我在一个项目中遇到了OP的一个紧急需求,在那里我需要通过源化定义环境的shell脚本来配置Perl脚本(实际上可以是任何语言)。
个人而言,我很少使用源化来做除了配置和环境设置之外的事情(我知道的唯一另一个好处是用于在shell脚本中导入函数,但我可能会错过一些创造性的用法)。
我的解决方案是从一个启动器脚本(在shell中)源化配置脚本,然后在同一个启动器脚本中exec Perl脚本(有效地用Perl脚本替换启动器脚本,从而避免创建子进程)。
# configuration_script.sh
export MY_ENV_VAR1=value1
export MY_ENV_VAR2=value2

# launcher_script.sh
. configuration_script.sh # source the configuration
exec /path/to/main_script.pl "$@" # could be any other language here (Python, Tcl, Ruby, C, Java...)

"

\"$@\"允许将启动程序脚本的命令行参数传递到主脚本。

然后由主脚本作者检索环境变量(例如,在Perl中使用$ENV{MY_ENV_VAR1})。

"

0

在Perl 5中无法源化shell文件。源化字面上是在目标shell中运行shell文件中的命令;但是,打开和读取文件非常简单:

#!/usr/bin/perl

use strict;
use warnings;

use Carp;

sub source {
    my $file = shift;
    open my $fh, "<", $file
        or croak "could not open $file: $!";

    while (<$fh>) {
        chomp;
        #FIXME: this regex isn't quite good enough
        next unless my ($var, $value) = /\s*(\w+)=([^#]+)/;
        $ENV{$var} = $value;
    }
}

source "./test1.sh";

print "$ENV{DATE}\n";

0

嗯...下面这个算作作弊吗?

#!/bin/sh
. ./test1.sh # source the test script
echo Bash says $DATE
export DATE;      # KEY to have the below Perl bit see $ENV{DATE}
perl <<'ENDPERL';
    print "Perl says $ENV{DATE}\n";
ENDPERL

问题在于,获取sh文件可能会执行任何操作,而不仅仅是将值X分配给变量Y...

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