如果向 STDERR 写入任何内容,我如何运行系统命令并死掉?

发布于 2024-10-06 03:45:28 字数 549 浏览 10 评论 0原文

我正在编写一个使用外部脚本的 Perl 脚本。外部脚本必须从特定目录运行,因此我发现以下内容很有用:

use IPC::System::Simple qw(capture);

my @args = ('external script path...', 'arg1', ...);
my $out = capture( [0], "cd $dir ; @args" );

有时外部脚本将内容写入 STDERR,但仍然返回 0。我希望捕获这些时间并 confess (或 die )。由于我不控制外部脚本的返回值,我想也许我可以捕获它的 STDERR,所以我会得到这样的结果:

my ($out, $err) = cool_capture( [0], "cd $dir ; @args" );
say "Output was: $out";
if ($err) {
 die "Error: this was written to STDERR: $err";
}

我能做什么?

I'm writing a Perl script which uses an external script. The external script must run from a specific directory so I found the following useful:

use IPC::System::Simple qw(capture);

my @args = ('external script path...', 'arg1', ...);
my $out = capture( [0], "cd $dir ; @args" );

Sometimes the external script writes stuff to STDERR but still returns 0. I wish to capture these times and confess (or die). Since I don't control the return value of the external script, I thought maybe I could capture its STDERR so I'll have something like this:

my ($out, $err) = cool_capture( [0], "cd $dir ; @args" );
say "Output was: $out";
if ($err) {
 die "Error: this was written to STDERR: $err";
}

What can I do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

满意归宿 2024-10-13 03:45:28

这是涵盖。

假设 test_app 是一个程序,它向 stdout 输出一行,向 stderr 输出一行:

use IPC::Open3;
use Symbol 'gensym';

my($wtr, $rdr, $err);
$err = gensym;

my $pid = open3($wtr, $rdr, $err, 'test_app');

waitpid($pid, 0);
my $status = $? >> 8;

my $stdout = <$rdr>;
my $stderr = <$err>;

print "out output: $stdout\n";
print "err output: $stderr\n";

print "Exit code: $status\n";

编辑:根据更新的请求以包括捕获退出代码。您也可以询问 perldoc IPC::Open3 ,其中写着

waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;

And ,您无论如何都应该阅读其中的警告和警告。

This is covered in the Perl FAQ.

Presuming test_app is a program that outputs one line to stdout and one line to stderr:

use IPC::Open3;
use Symbol 'gensym';

my($wtr, $rdr, $err);
$err = gensym;

my $pid = open3($wtr, $rdr, $err, 'test_app');

waitpid($pid, 0);
my $status = $? >> 8;

my $stdout = <$rdr>;
my $stderr = <$err>;

print "out output: $stdout\n";
print "err output: $stderr\n";

print "Exit code: $status\n";

EDIT: Per the request updated to include capturing the exit code. You could also have asked perldoc IPC::Open3 which says

waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;

And which you should read anyway for its cautions and caveats.

陌伤浅笑 2024-10-13 03:45:28

如果有大量输出被写入 stdout 和/或 stderr,或者您正在读取和写入进程。您需要更加小心地处理 I/O 以避免各种阻塞问题。

my ($wtr, $rdr, $err) ;

my $pid = IPC::Open3::open3($wtr, $rdr, $err, @_);

close($wtr);

my $stdout = '';
my $stderr = '';

my $s = IO::Select->new;

$s->add($rdr) if $rdr;
$s->add($err) if $err;

while (my @ready = $s->can_read) {

    foreach my $ioh (@ready) {

        my $bytes_read = sysread($ioh, my $chunk = '', 1024);

        die "read error: $!" unless $bytes_read >= 0;

        if ($bytes_read) {
           ($ioh eq $rdr? $stdout: $stderr) .= $chunk;
        }
    else {
            $s->remove($ioh);
        }
    }
} 

my $pid1;
for (;;) {

    last if kill(0, $pid);

    $pid1 = wait();
    #
    # Wait until we see the process or -1 (no active processes);
    #
    last if ($pid1 == $pid || $pid1 <= 0);
}

在关闭进程之前完成阅读。如果您要写入进程的标准输入,则还需要将 $wtr 和 syswrite 添加到上面的选择循环中。

编辑

理由:

对于简单的情况,上述内容可能有点过分了。当您可能移动超过几 K 的数据时,这种对输入和输出的高级处理就会发挥作用。

例如,如果您正在执行“df”命令,则不需要它。

然而,当任何 stdin、stdout 或 stderr 的系统缓冲区填满时,阻塞就可能发生,事情会变得更加复杂。

如果子进程填满了 stderr 和/或 stdout 缓冲区,它可能会阻塞并等待您清除它们。但是,如果您在从 stdout 或 stderr 读取之前等待进程完成;那是一个僵局。您可能会看到系统调用永远不会完成,子进程也永远不会完成。

如果正在写入 stdin,但子进程无法使用输入,则存在类似的死锁可能性。在子进程消耗输入并写入标准输出的“管道”情况下,这种情况尤其可能发生。

选择循环是关于逐步清除缓冲区以避免阻塞。同时监视 stdout 和 stderr。

如果要写入 stdin 并从 stdout(管道)读取,则需要保持 stdout 和 stderr 清晰,并且仅在准备好接收输入时写入 stdin。

只需等待该过程完成,然后读取 stdout/stderr 可能就可以在 90% 的情况下工作。此回复只是为了在事情变得更加复杂并且进程开始阻塞或陷入死锁时为您提供一个可以去的地方。

EDIT2

至于使用哪个,我会说从简单开始,努力测试。

采用 Sorpigal 的方法,但尝试使用更高的数据量以及您在实时系统中所期望的更困难的负载和条件进行压力测试。

If significant output is being written to stdout and/or stderr or you're both reading and writing to the process. You need to be a lot more careful with your I/O handling to avoid various blocking problems.

my ($wtr, $rdr, $err) ;

my $pid = IPC::Open3::open3($wtr, $rdr, $err, @_);

close($wtr);

my $stdout = '';
my $stderr = '';

my $s = IO::Select->new;

$s->add($rdr) if $rdr;
$s->add($err) if $err;

while (my @ready = $s->can_read) {

    foreach my $ioh (@ready) {

        my $bytes_read = sysread($ioh, my $chunk = '', 1024);

        die "read error: $!" unless $bytes_read >= 0;

        if ($bytes_read) {
           ($ioh eq $rdr? $stdout: $stderr) .= $chunk;
        }
    else {
            $s->remove($ioh);
        }
    }
} 

my $pid1;
for (;;) {

    last if kill(0, $pid);

    $pid1 = wait();
    #
    # Wait until we see the process or -1 (no active processes);
    #
    last if ($pid1 == $pid || $pid1 <= 0);
}

Finish reading before you shutdown the process. If you're writing to the process's stdin, you'd also need to add $wtr and syswrite to the above select loop.

EDIT

Rationale:

The above is probably overkill for simple cases. This advanced handling of input and output comes into play when you're likely to move more than a few K of data.

You wouldn't need it if you were executing a 'df' command for example.

However, it's when system buffers for any of stdin, stdout or stderr fill up that blocking becomes likely and things can get more involved.

If the child process fills up the stderr and/or stdout buffers, it'll likely block and wait for you to clear them. But if you're waiting for the process finish before you read from stdout or stderr; thats a deadlock. You'll likely to see that the system call never finishes and the child process never completes.

There's a similar possibility of deadlock if stdin is being written to, but the child process is unable to consume the input. This is particularly likely in a 'pipe' situation where the child process is consuming input and writing to stdout.

The select loop is about progressively clearing the buffers to avoid blocking. Both stdout and stderr are monitored concurrently.

If you're writing to stdin and reading from stdout (a pipe), you'll want to keep stdout and stderr clear and only write to stdin when its ready to receive input.

Simply waiting for the process to finish, then reading stdout/stderr probably works 90% of the time. This reply is just to give you somewhere to go if things get more complicated and processes start to block or go into deadlock.

EDIT2

As for which to use, I'd say start simple, test hard.

Go with Sorpigal's approach, but try to stress test with higher data volumes and under more difficult loads and conditionals that you'd ever expect in a live system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文