为将由父进程启动的所有子进程重定向STDOUT和STDERR

问题描述 投票:1回答:1

在Perl(使用5.30.0版)中,是否可以以编程方式(即在我的Perl代码中)更改某些设置,该设置将导致STDOUT和STDERR被重定向到我的Perl进程将启动的任何子进程的文本文件。从那时开始?这里的困难在于,我无法控制实际上启动那些子进程的代码(如果我这样做了,那将是微不足道的)。我的希望是,可以在Perl进程中设置某种IO标志,以使所有新启动的子进程将其STDOUT / STDERR通道重定向到我选择的目的地(除非system()/ exec() /用来启动它们的任何调用都会明确提供另一个STDOUT / STDERR目标)。

我需要的(高度简化)用例如下。我编写了一个通用库函数,该函数执行调用方指定的任务,除其他外,将用户提供的任务生成的所有STDOUT / STDERR输出存储在文本文件中。以Perl函数参考的形式提供任务。到目前为止,我仅设法重定向了我的Perl进程生成的STDOUT / STDERR输出:

    local *STDOUT;
    local *STDERR;

    open( STDOUT, '>', $buildLogFilePath ) or die sprintf(
        "ERROR: [%s] Failed to redirect STDOUT to \"%s\"!\n",
        $messageSubject,
        $buildLogFilePath
    );

    open ( STDERR, '>&', STDOUT ) or die sprintf(
        "ERROR: [%s] Failed to redirect STDERR to \"%s\"!\n",
        $messageSubject,
        $buildLogFilePath
    );

只要用户提供的功能没有启动任何子进程,我的功能就可以按需运行,但是当它确实启动了子进程时,该进程的输出将仅有效地进入默认的STDOUT / STDERR通道破坏了我的功能。

windows perl
1个回答
0
投票

打开文件句柄时,它使用下一个可用的文件描述符。 STDIN,STDOUT和STDERR通常分别与fd 0、1和2关联。如果进程中没有其他打开的句柄,则由open创建的下一个句柄将使用文件描述符3。

如果将fd 3与STDOUT相关联,很多事情将继续起作用。这是因为Perl代码通常处理Perl文件句柄而不是文件描述符。例如,print LIST实际上是print { select() } LIST,默认情况下与print STDOUT LIST相同。因此,您的更改通常在Perl中有效。

但是,当您执行程序时,所有得到的过程都是文件描述符。它得到fd 0、1和2。它甚至可能得到fd 3,但它并不在乎。它将输出到fd 1。


一个简单的解决方案是删除local *STDOUT; local *STDERR;

[*STDOUT是一个小球,一个包含*STDOUT{IO}的结构,该句柄。

通过使用local *STDOUT,您将用一个空的glob替换该glob。原始文件不会被销毁-当local超出范围时将被恢复-因此与非匿名glob相关联的Perl文件句柄将不会关闭,因此与该句柄相关联的fd将不会关闭被关闭,因此后续的open无法重用该fd。

[如果避免执行local *STDOUT,则意味着您正在将打开的手柄传递给openopen在这种情况下的行为特别:它将“重新打开”已经与Perl句柄关联的fd,而不是创建新的fd。

$ perl -e'
   open( local *STDOUT, ">", "a" ) or die;
   open( local *STDERR, ">&", STDOUT ) or die;
   print(fileno(STDOUT), "\n");
   system("echo foo");
'
foo

$ cat a
3

$ perl -e'
   open( STDOUT, ">", "a" ) or die;
   open( STDERR, ">&", STDOUT ) or die;
   print(fileno(STDOUT), "\n");
   system("echo foo");
'

$ cat a
1
foo

如果您希望重定向是临时的。您必须使用文件描述符。

$ perl -e'
   open( local *SAVED_STDOUT, ">&", STDOUT) or die;
   open( STDOUT, ">", "a" ) or die;
   print(fileno(STDOUT), "\n");
   system("echo foo");
   open( STDOUT, ">&", SAVED_STDOUT) or die;
   print(fileno(STDOUT), "\n");
   system("echo bar");
'
1
bar

$ cat a
1
foo
© www.soinside.com 2019 - 2024. All rights reserved.