修改子进程的输出以添加时间戳

时间:2013-11-05 18:55:21

标签: linux perl unix fork pipe

我有一个perl脚本,用于处理子进程。

sub my_exec{
    my($args,$stdout, $stderr) = @_;
    my $processes = fork();
    die("Cant fork") unless defined($processes);
    if(processes == 0){
        if(defined $stdout){
            close(STDOUT);
            open STDOUT, $stdout;
        }
        if(defined $stderr){
            close(STDERR);
            open STDERR, $stderr;
        }
        exec @$args;
    }else{
                ...
    }
}

我的主要问题是我想在stderr的每一行输出中添加一个时间戳。我想知道是否可以在这里完成。正如你所看到的,stderr并不总是被改变。我假设我可以通过某种管道来做到这一点?我还想重定向父脚本(守护进程,stdout和stderr重定向到文件)也使用时间戳。

由于

2 个答案:

答案 0 :(得分:2)

说你写下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: $!";
  }
}

主要观点在documentation on open

中描述
  

如果您在命令-上打开管道(即,使用打开的单参数或双参数形式指定|--|),则隐式{{ 1}}已完成,因此open返回两次:在父进程中它返回子进程的pid,在子进程中它返回(已定义的)fork。使用0defined($pid)确定开放是否成功。

隐式//的要点是在父进程和子进程之间建立管道。

  

文件句柄对父级的行为正常,但该文件句柄的I / O是从子进程的fork传送的。在子进程中,文件句柄未打开 - I / O从新的STDOUT发生。

这几乎是完美的,除了你想要修改标准错误,而不是标准输出。

这意味着我们需要保存父级STDOUT,以便孩子可以恢复它。这就是STDOUT发生的事情。

将孩子(重定向)$oldout复制到其STDOUT上,安排基础守护程序的标准错误通过管道运行,父管读取,修改和输出。

一个稍微棘手的问题是处理重定向的位置。如果呼叫者想要重定向STDERR,则需要在孩子身上发生。但是要重定向STDOUT,父母需要这样做,因为这会让父母有机会修改流。

完整示例的代码具有以下形式。你提到了一个守护进程,所以我启用了Perl’s dataflow analysis known as taint mode

STDERR

使用简单的#! /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

输出转到单独的文件。

<强> 1。 #! /usr/bin/env perl print "Hello world!\n"; warn "This is a warning.\n"; die "All done.\n";

Hello world!

<强> 2。 my-stdout

Tue Nov  5 17:58:20 2013: This is a warning.
Tue Nov  5 17:58:20 2013: All done.
./wrapper: exit status 255 from ./mydaemon at ./wrapper line 23.

答案 1 :(得分:1)

fork是如此之低。 IPC :: Open3是你应该使用的最小值。

use IPC::Open3 qw( open3 );

open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
open(local *CHILD_STDOUT, '>', $stdout) or die $!;

my $pid = open3(
   '<&CHILD_STDIN',
   '>&CHILD_STDOUT',
   \local *CHILD_STDERR,
   $prog, @$args
);

open(my $stderr_fh, '>', $stderr) or die $!;
while (<CHILD_STDERR>) {
   print $stderr_fh $ts . $_;
}

waitpid($pid, 0);