2016-03-06 141 views
1

我有一个模拟tee命令的perl脚本,所以我可以将输出写入终端和日志文件。它的工作原理是这样的(错误检查& c略)。perl父进程挂起等待子进程读取stdin

$pid = open(STDOUT, '-|'); 
# Above is perl magic that forks a process and sets up a pipe with the 
# child's STDIN being one end of the pipe and the parent's STDOUT (in 
# this case) being the other. 
if ($pid == 0) 
{ 
    # Child. 
    # Open log file 
    while (<STDIN>) 
    { 
     # print to STDOUT and log file 
    } 
    #close log files 
    exit; 
} 
# parent 
open STDERR, '>&STDOUT'; 
# do lots of system("...") calls 
close STDERR; 
close STDOUT; 
exit; 

这有时会挂起,并不约而同如果你看一下进程和堆叠说的过程中,父母是悬在关闭的一个,等待孩子退出,而孩子挂读点从一个文件(它必须是STDIN,因为没有其他文件)。

我对如何处理这件事感到茫然。如果你从一个没有连接到控制台的shell运行程序,这个问题似乎就会发生 - 在一个正常的shell中运行脚本工作正常 - 并且该脚本中最近更改的唯一代码是添加打开/关闭文件只是为了触摸它(并且在脚本到达这个'tee'代码之前)。

有没有人有过这样的问题,和/或有什么我可以做的解决这个问题的建议?谢谢。

+0

这是_always_分离时发生吗? STDOUT是否早于重定向 - 在cron-ed命令行或程序中? – zdim

+0

我看不出问题,对我而言,它无论如何都有效。我更新了自己想要的具体内容,并使用了我正在使用的确切代码。我正在考虑缓冲/ STDIN越来越混乱。例如,最后的打印(最后一个缓冲区)不会被刷新。 – zdim

回答

0

好了,经过一些实验似乎开幕STDOUT直接似乎是至少部分原因。我的代码现在读这样的:

$pid = open($handle, '|-'); 
if ($pid == 0) 
{ 
    # Child. 
    # Open log file 
    while (<STDIN>) 
    { 
     # print to STDOUT and log file 
    } 
    #close log files 
    exit; 
} 
# parent 
open my $oldout, '>&STDOUT'; 
open my $olderr, '>&STDERR'; 
open STDOUT, '>&', $handle; 
open STDERR, '>&', $handle; 
# do lots of system("...") calls 
open STDOUT, '>&', $oldout; 
open STDERR, '>&', $olderr; 
close $handle or die "Log child exited unexpectedly: $!\n"; 
exit; 

其中,如果不出意外,看起来比较清爽(但仍比梅西耶我想,因为我不知道该怎么做,如果其中任何的DUP都有一个错误)。但是我仍然不清楚为什么在代码中更早地打开和关闭句柄对这一点做出了很大的改变。

0

更新

我无法重现犯罪行为。对我来说,它可以像终端和cron一样运行。 (如果使用后台运行,它将在进行fd 1,2时暂停)。考虑到控制台重定向时出现的问题,这是我想尝试的方法

1)在子代和父代中明确关闭缓冲

2)关闭STDIN while循环

3)陷阱SIGPIPE看到的东西是否变成了

*)是否有其它的代码实际上得到绊倒后明确?


下面是对首先发布的解决方法的想法。


看来这个问题是发生了什么STDOUT与重定向控制台什么孩子则可以或不可以用它做。我还没有知道那是什么,但同时我认为,至少应该避免这个问题。

如果没有tty,孩子可以将输出意图写入STDOUT到另一个文件。此文件可以是父级的重定向(如果已知,附加到),也可以是父级稍后加入的另一个明确打开的文件。这是混乱,粗糙,它改变了设计,但它可能使孩子退出(不处理STDOUT这可能是问题的根源)。

或者,父母可以控制此操作,当没有tty时打开记录器子项,这将完全侧重于情况,但是离设计更远。

这些都不能解决问题,但它可能会避免它。


以供参考,这是表现为从CMDLINE进出的cron的预期代码

$| = 1; 
$logfile = '/path/logfile.out'; 

$pid = open(STDOUT, '|-'); 
if ($pid == 0) { 
    # $| = 1; 
    open $log_fh, '>', $logfile; 
    while (<STDIN>) { 
     print $_; 
     print $log_fh $_; 
    } 
    # close STDIN; 
    close $log_fh; 
    exit; 
} 
open STDERR, '>&STDOUT'; 

print "After the fork.\n"; 
warn "A warn --"; 

close STDERR; 
close STDOUT; 
exit; 
+0

当未连接到控制台时,STDOUT将转到文件。没有STDIN,但它不使用或需要STDIN。另外,它不会在运行'script&'的背景中运行。当它发生在我身上时,它就像一个cron工作一样运行。 –

+0

,打开电话分岔一个孩子,并创建一个管道,孩子得到STDIN。问题不是它挂起,因为没有STDIN,问题是当它到达主程序的出口时它会挂起(它可以在产生正确的所有生成之前产生大量的输出) –

+0

孩子STDIN是来自父母的STDOUT。如果我没有使用STDIN,那么它根本就不起作用。 –