如何像 Raku 一样在 Perl 中运行 shell 命令?

con*_*con 6 perl raku

我在 Raku 中有出色的代码:

#!/usr/bin/env perl6
CONTROL {
    when CX::Warn {
        note $_;
        die
    }
}
use fatal;
role KeyRequired {
    method AT-KEY (\key) {
        die "Key {key} not found" unless self.EXISTS-KEY(key);
        nextsame
    }
}

sub execute ($cmd) {
    put $cmd;
    my $proc = shell $cmd, :err, :out;
    if $proc.exitcode != 0 {
        put 'exit code = ' ~ $proc.exitcode;
        put 'stderr ' ~ $proc.err.slurp;
       put 'stdout ' ~ $proc.out.slurp;
        die
    }
}

execute "ls *.p6"
Run Code Online (Sandbox Code Playgroud)

我说“优秀”是因为 Raku 版本运行命令,返回退出代码,并在需要时打印 stdout/stderr,并且所有这些都以易于阅读和易于理解的方式进行。

通读IPC::Run https://metacpan.org/pod/IPC::Run的 Perl5 手册,我遇到了似乎是最好的 Perl5 方法,但我发现那里使用的方法不太容易阅读并且比Raku的做事方式更理解。

通读IPC::Run我能找到的最好的手册是:

#!/usr/bin/env perl

use strict;
use warnings FATAL => 'all';
use feature 'say';
use autodie qw(:all);
use IPC::Run qw(run timeout);

sub execute {
    my $cmd = shift;

    my @cat = ('cat', __FILE__); # Raku doesn't need to split the string into an array
    run \@cat, \undef, \my $out, \my $err, timeout( 10 ) or die "cat: $?"; 
    if ($out ne '') {
        say "\$out = $out";
    }
    if ($err ne '') {
        say "\$err = $err";
    }
}

execute("cat " . __FILE__);

execute("cat __Fle");  #intentionally wrong to produce an error
Run Code Online (Sandbox Code Playgroud)

我怎样才能重写 Perl5,使它像 Raku 代码一样容易阅读和使用?

bri*_*foy 3

您不公平地加载了 Perl 5 示例,其中包含大量额外的内容,并且您没有处理 Raku 代码中的许多内容。例如,无论变量中有什么,您都在 Raku 中输出结果,但在 Perl 5 中测试变量。

你的 Perl 5 看起来更像这样:

use v5.30;
use IPC::Run qw(run timeout);

sub execute {
    my @command = @_;
    run \@command, \undef, \my $out, \my $err, timeout( 10 ) 
        or die "cat: $?"; 
    say "\$out = $out";
    say "\$err = $err";
}

execute("cat ", __FILE__);
Run Code Online (Sandbox Code Playgroud)

ikegami 在他的 Pastebin 链接中提供了这个版本:

sub execute {
    my ($command) = @_;
    if (! run $command, \undef, \my $out, \my $err, timeout( 10 ) ) {
       say "exit code = $?";
       say "stderr $err";
       put "stdout $out";
       die "Died";
   }
}
Run Code Online (Sandbox Code Playgroud)

在这两种情况下都有一件有趣的事情需要注意。如果退出代码不为零,则假设发生错误(Raku 假设如此,这就是为什么你必须担心结果不会下降)。然而,许多有用的程序并不遵循该约定。例如,git merge base使用退出值 1 表示“不是祖先”,所有大于 1 的退出值表示错误。命令行grep类似。sendmail 的退出代码为 75,表示某些事情没有成功,但稍后会重试。

Raku 对此有自己的看法,它忽略了这类事情,并且不允许您告诉 Proc 它应该接受哪些退出值作为成功退出。Perl 5 并不那么固执己见。使用or dieor! ...实际上是在说“退出代码不为零”,但这并不是一个足够好的描述。在很多情况下你都可以侥幸逃脱,但至少 Perl 5 不会为你做决定。如果您扩展 Raku 示例来检查文字值并确定是否成功,它会看起来很混乱。


但是,请注意 Raku 的shell文档指出它不安全,您应该改用它run

无论如何,我认为 Raku 的进程间通信并不那么值得信赖。很多时候,我认为它的IPC设计被忽视了。例如,更改 Perl 6 的 $*OUT 是否会更改子进程的标准输出?。我在 bug 报告和 Stackoverflow 中还有其他几个 IPC 问题,但几乎没有一个得到满意的答案。我认为这主要是因为没有人考虑得那么多。诚然,Raku 是由一个小团队开发的,它是一个大项目,但当涉及到生产编程时,这不是一个因素。

还有一些 Raku shell 的怪异之处: