如何在perl中执行multiline shell命令?

rog*_*ger 1 shell perl

我想在perl中运行shell命令如下:

tar --exclude="*/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .
Run Code Online (Sandbox Code Playgroud)

但似乎perl无法执行此操作:

`tar --exclude="cv/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .`;
Run Code Online (Sandbox Code Playgroud)

这是错误:

tar: Must specify one of -c, -r, -t, -u, -x
sh: line 1: --exclude=*/vendor: No such file or directory
sh: line 2: --exclude=.git: command not found
sh: line 3: -zvcf: command not found
Run Code Online (Sandbox Code Playgroud)

似乎perl将每一行视为一个命令.

Bor*_*din 8

更新

我道歉.我原来的诊断错了

在这样的帖子中很难清楚地表达包含Perl转义字符的字符串的内容.如果您不清楚以下任何内容,请写下评论来说明.我希望我没有让事情变得不必要地复杂化

我下面的原始解决方案仍然有效,并且可以让您更好地控制命令的内容,但是我为什么OP的代码对他们不起作用的原因是错误的,并且真相提供了其他解决方案

的问题是,反引号(或内容qx/.../)的作为评价双引号字符串,这意味着Perl的变量和逸出样序列\t\x20在执行字符串之前被扩展.其中一个后果是,如果后跟一个文字换行符,则删除反斜杠,只留下换行符

这意味着像这样的声明

my $output = `ls \
-l`;
Run Code Online (Sandbox Code Playgroud)

将被预处理"ls \n-l"并且将不再包含向shell发出应该删除换行所需的反斜杠(或者实际上是首先将命令传递给shell)

除了我在下面的原始帖子中描述的直接操作命令字符串外,还有两种解决方案.第一种是通过将其加倍来逃避反斜杠本身,就像这样

my $output = `ls \\
-l`;
Run Code Online (Sandbox Code Playgroud)

这将防止它被Perl删除.这会将反斜杠换行序列传递给shell,这将正常删除它

另一种是使用qx'...'单引号分隔符而不是反引号,这将阻止内容被处理为双引号字符串

my $output = qx'ls \
-l';
Run Code Online (Sandbox Code Playgroud)

除非您在要插入的字符串中使用了Perl变量,否则这将正常工作



原帖

问题是shell在执行之前从命令字符串中删除了前缀为反斜杠的换行符.没有该步骤,命令无效

所以你必须自己在Perl中做同样的事情,为此你必须把命令放在一个临时变量中

use strict;
use warnings 'all';

my $cmd = <<'END';
tar --exclude="*/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .
END

$cmd =~ s/\\\n//g;

my $output = `$cmd`;
Run Code Online (Sandbox Code Playgroud)

当然不需要反斜杠; 您可以在执行命令之前简单地使用换行符并删除它们

或者您可能更喜欢将操作包装在子例程中,如下所示

use strict;
use warnings 'all';

my $output = do_command(<<'END');
tar --exclude="*/node_modules" \
--exclude="*/vendor" \
--exclude='.git' \
-zvcf /tmp/robot.tgz .
END

sub do_command {
    my ($cmd) = @_;
    $cmd =~ s/\\\n//g;
    `$cmd`;
}
Run Code Online (Sandbox Code Playgroud)