【问题标题】:How can I check the status of the first program in pipeline in Perl's system()?如何在 Perl 的 system() 中检查管道中第一个程序的状态?
【发布时间】:2010-11-22 19:40:23
【问题描述】:
perl -e 'system ("crontab1 -l");print $?'

按预期返回 -1(程序 crontab1 不存在)

perl -e 'system ("crontab1 -l|grep blah");print $?'

返回 256。

检查第一个(或两个)程序状态的方法是什么?

【问题讨论】:

    标签: perl ipc


    【解决方案1】:

    您将获得整个命令的退出状态,正如您应该做的那样。如果您想要单独的退出状态,您将不得不单独运行这些命令。

    #!/usr/bin/perl -e
    system("crontab1 -l > /tmp/junk.txt"); print $?;
    system("grep blah /tmp/junk.txt"); print $?;
    

    举个例子。

    【讨论】:

    • @Michael Kohne,“输出”是指“终止状态”吗? OP 不想区分他的子进程的输出,而是它们的退出状态。
    • 这很有帮助,但现在我需要对文件访问进行错误处理。例如: perl -e 'system("crontab1 -l > /blah");打印 $?;'仍然给我 256。我想我需要进一步拆分它:)
    • 不需要临时文件。 JB 下面有一个更好的答案。
    【解决方案2】:

    如果您容忍使用 system 以外的其他内容,则有更简单的解决方案。例如the results method in IPC::Run返回链的所有退出码。

    【讨论】:

    • 我确信 IPC::Run 很棒,但是当我在寻找类似的东西时,我首先找到了 Proc::SafeExec。它工作得很好,虽然看起来比 IPC::Run 更冗长。很高兴有选择:-)
    • 好信息,谢谢,但 IPC 目前还不是项目的一部分,此时不希望引入新的依赖项。
    • @vt: 你可以用 open(..., "-|", ...) 或 open(..., "|-", ...) (fork STDIN/STDOUT 附加在子进程之间)和 exec(...),但 IPC::Run 或 Proc::SafeExec 更容易使用。如果我是你,我会克服你的偏好,而不是使用临时文件(这是另一种依赖类型)。我这里有一些代码:perlmonks.org/?node_id=246397
    【解决方案3】:

    记住,你应该使用 $?>>8 来获取退出代码,而不是 $?

    perl -e 'system("set -o pipefail;false | true");print $?>>8,"\n"'
    

    1

    这个(“pipefail”)只有在你的 shell 是 bash 3 时才有效。Cygwin 和 linux 附带它;不知道mac。

    您应该知道 256 是一个错误返回。 0 是你会得到的成功:

    perl -e 'system("true");print $?>>8,"\n"'
    

    0

    我不知道系统会为未找到的单个命令返回 -1,但 $?>>8 在这种情况下仍应为非零。

    【讨论】:

    • 你真的应该使用 POSIX::WEXITSTATUS 而不是右移
    • 即使右移,它仍然不是我想要的 -1。
    • 255 是您要查找的 -1,因为 ((int)-1 & 0377)(这是 exit() 将返回值转换为的内容)是 0xff,或 255
    【解决方案4】:

    [这是作为对另一个 question 的回答而编写的,该 question 已作为此邮件的副本关闭。]

    执行 shell 命令需要执行 shell。为此,

    system($shell_command)
    

    等价于

    system('/bin/sh', '-c', $shell_command)
    

    因此,您的所有示例都运行一个程序 (/bin/sh)。如果你想要多个孩子的退出状态,你需要有多个孩子!

    use IPC::Open3 qw( open3 );
    
    open(local *CHILD1_STDIN, '<', '/dev/null')
       or die $!;
    
    pipe(local *CHILD2_STDIN, local *CHILD1_STDOUT)
       or die $!;
    
    my $child1_pid = open3(
       '<&CHILD1_STDIN',
       '>&CHILD1_STDOUT',
       '>&STDERR',
       'prog1', 'arg1', 'arg2',
    );
    
    my $child2_pid = open3(
       '<&CHILD2_STDIN',
       '>&STDOUT',
       '>&STDERR',
       'prog2', 'arg1', 'arg2',
    );
    
    my @pipe_status = map { waitpid($_, 0) } $child1_pid, $child2_pid;
    

    IPC::Open3 级别相当低。 IPC::Run3 和/或 IPC::Run 可能会使这更容易。 [更新:确实,IPC::Run does]。

    【讨论】:

    • @Wakan Tanka,此答案是针对您的问题而写的。
    • 谢谢,但我对语法有点困惑。例如。您正在使用 IPC::Open3 模块中的 open3 函数,但在代码中您使用的是经典的 open 函数。
    • @Wakan Tanka,open 确实被使用了,但open3 也是如此
    【解决方案5】:

    如果要检查状态,请不要将它们全部放在同一个系统中。打开一个到第一个程序的读取管道以获取其输出,然后打开另一个管道到另一个程序。

    【讨论】:

      【解决方案6】:

      操作系统只为最后执行的程序返回退出状态,如果操作系统不返回,perl 无法报告。

      除了单独运行每个程序并使用临时文件而不是管道之外,我不知道有任何方法可以获取管道中早期程序返回的退出代码。

      【讨论】:

        【解决方案7】:

        检查第一个(或两个)程序状态的方法是什么?

        没有这样的方法,至少,不是像你构建的那样。如果你必须知道这些事情,你可能必须自己通过 fork()、exec() 和 waitpid() 来管理子进程。

        以下是您的代码片段中发生的大致情况。

        1. perlsystem() 生成一个 shell,perl *wait()*s 让该子进程终止。

        2. shell 设置管道:

          1. 管道读取端的子shell *exec()*s grep
          2. 子shell 无法在$PATH 中的任何位置找到crontab1,并且*exit()*s 127(在我的系统上,即127 是表示找不到要运行的程序的shell)。李>
        3. grep 在其输入中检测到文件结尾,并且没有匹配任何内容,*exit()*s 1。

        4. shell *exit()*s 带有管道中最后一个进程的退出代码,同样是 1。

        5. perl 检测到 shell 的退出代码 1,在 $? 中编码为 256。
          (256 >> 8 == 1)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-06-02
          • 2014-06-04
          • 2015-08-06
          • 1970-01-01
          • 2014-09-04
          • 2012-05-18
          • 1970-01-01
          相关资源
          最近更新 更多