假设你写my_exec
如下。
sub my_exec {
my($args,$stdout,$stderr) = @_; # caller untaints
open my $oldout, ">&STDOUT" or die "$0: save STDOUT: $!";
my $pid = open my $pipe, "-|" // die "$0: fork: $!";
if ($pid) {
if (defined $stderr) {
open STDERR, ">", $stderr or die "$0: open: $!";
}
while (<$pipe>) {
print STDERR scalar(localtime), ": ", $_;
}
close $pipe or die $! ? "$0: error closing $args->[0] pipe: $!"
: "$0: exit status " . ($? >> 8) . " from $args->[0]";
}
else {
open STDERR, ">&STDOUT" or die "$0: pipe STDERR: $!";
if (defined $stdout) {
open STDOUT, ">", $stdout or die "$0: open: $!";
}
else {
open STDOUT, ">&", $oldout or die "$0: restore STDOUT: $!";
}
exec @$args or die "$0: exec @$args: $!";
}
}
要点在以下文档open
中进行了描述:
如果您在命令上打开一个管道-
(即,指定一个|-
或-|
两个参数形式的 open),则执行隐式fork
操作,因此 open 返回两次:在父进程中,它返回子进程的 pid ,并在子进程中返回 (a defined) 0
。使用defined($pid)
或//
确定打开是否成功。
隐含的要点fork
是在父进程和子进程之间建立管道。
文件句柄对父进程表现正常,但该文件句柄的 I/O 是从STDOUT
子进程的管道传输的。在子进程中,文件句柄没有打开——I/O 从新的STDOUT
.
这几乎是完美的,除了你想修改标准错误,而不是标准输出。
这意味着我们需要保存父母的STDOUT
,以便孩子可以恢复它。这就是正在发生的事情$oldout
。
将子进程的(重定向的)STDOUT
复制到其上,以STDERR
安排底层守护进程的标准错误通过管道运行,父进程读取、修改和输出管道。
一个有点棘手的地方是处理重定向的地方。如果调用者想要重定向STDOUT
,那需要发生在孩子身上。但是要重定向STDERR
,父级需要这样做,因为这使父级有机会修改流。
完整示例的代码如下所示。你提到了一个守护进程,所以我启用了Perl 的数据流分析,称为 taint mode。
#! /usr/bin/perl -T
use strict;
use warnings;
use v5.10.0; # for defined-or //
$ENV{PATH} = "/bin:/usr/bin";
sub my_exec {
# paste code above
}
#my_exec ["./mydaemon"];
#my_exec ["./mydaemon"], "my-stdout";
my_exec ["./mydaemon"], "my-stdout", "my-stderr";
用一个简单mydaemon
的
#! /usr/bin/env perl
print "Hello world!\n";
warn "This is a warning.\n";
die "All done.\n";
输出到单独的文件。
1 my-stdout
.:
你好世界!
2 my-stderr
.:
2013 年 11 月 5 日星期二 17:58:20:这是一个警告。
2013 年 11 月 5 日星期二 17:58:20:全部完成。
./wrapper:在 ./wrapper 第 23 行从 ./mydaemon 退出状态 255。