perl在退出时挂起(关闭文件句柄后)

时间:2009-10-16 16:20:52

标签: linux perl process ipc pipe

我有一个功能(简而言之):

my $file = IO::File->new("| some_command >> /dev/null 2>&1") 
    or die "cannot open some_command for writing: $!\n";
...
undef $file;

现在我甚至都没有给$file写任何东西。目前,$file上根本没有其他操作。当我运行该程序时,它无法正常退出。我看到句柄已关闭,但我的程序仍在等待关闭进程。使用strace捕获:

close(6)                                = 0
rt_sigaction(SIGHUP, {SIG_IGN}, {SIG_DFL}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN}, {SIG_DFL}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_DFL}, 8) = 0
wait4(16861, ^C <unfinished ...>

如果我打开相同的阅读流程,我不会看到这个问题。

如何让程序退出?

编辑:到目前为止的建议是使用Expect库或通过ctrl + d完成输入流。但是我现在不想以任何方式与该程序进行交互。我希望它完全现在完成,而不再进行任何IO。这可能吗?

4 个答案:

答案 0 :(得分:9)

undef $file从文件句柄中删除引用计数,并使其符合垃圾回收的条件。如果$file是常规文件的句柄,并且在其他任何地方都没有对文件句柄的其他引用,则它应该按照IO::File中的说明进行操作。在这种情况下,$file是shell命令的句柄,并且可能存在对文件句柄的一些其他内部引用,以防止它被销毁。使用$file->close更安全,使您的意图更加清晰。

<小时/>

要在关闭文件句柄时终止命令不起作用,您需要进程ID。如果你调用了像

这样的命令
my ($file,$pid);
$pid = open($file, "| some_command >> /dev/null 2>&1");

然后你可以

kill 'TERM',$pid;

在您的计划结束时。我不知道如何从IO::File::new的返回值中提取进程ID。

答案 1 :(得分:6)

如果some_command正在等待输入,它可能会永远坐在那里等待输入。

从文档的内容来看,我认为它没有任何区别,但我总是在取消处理句柄之前执行$ file-&gt; close()而不是/。

编辑:发送它控制D? 也许some_command正在读取tty而不是stdin,就像passwd那样。如果你在那个领域,我建议你查看Expect。

控制D只是复制了关闭应该对命令行程序执行的零字节读取。

您是否尝试使用$ file-&gt; close()而不是undef?

答案 2 :(得分:0)

some_command是否会输入并处理它?比如grep? 还是提示?比如说... chfn? 它是否返回任何有用的信息?就像它已经完成的迹象一样?

如果是后者,您可能需要阅读Expect,以便您可以与之互动。

答案 3 :(得分:0)

这个丑陋,丑陋的黑客攻击会导致some_command成为init的父级,而不是留在perl的进程树中。 Perl不再需要等待任何进程,并且管道仍然有效 - yay UNIX。

my $file = IO::File->new("| some_command >> /dev/null 2>&1 &")

缺点:即使&失败,shell也会在some_command成功,因此您不会再收到任何错误。

    or die "cannot open some_command for writing: $!\n";  # now useless

如果some_command在stdin上获得EOF后立即退出(并且永远不会停止从stdin读取),但我希望这不是必需的。

$ cat | some_command
^D

那会挂起吗,你可以解决这个问题吗?