Perl关闭管道没有错误 [英] Perl closing pipe without error

查看:100
本文介绍了Perl关闭管道没有错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Perl执行外部程序,并且如果在运行时返回特定字符串,则希望结束其执行.下面的代码根据需要中断执行,但是,当执行最后一行(关闭)时,将返回错误消息.

I'm using Perl to execute an external programme and would like to end its execution if it returns a specific string while running. The code below interrupts the execution as desired, however an error message is returned when the last line (close) is executed.

open (my $out, "-|", "usfos $memory<input-$cpu.scr");
while (<$out>) {
    if ($_ =~ /MIN   STEP  LENGTH/) {
        last;
    }
}
close $out;

这是所打印错误的一部分(外部程序还会返回错误消息):

This is the part of the error that is printed (the external programme also returns error messages):

...forrtl: The pipe is being closed.
forrtl: severe (38): error during write, unit 6, file CONOUT$

所以我认为这是因为Perl试图写一个封闭的句柄.如何避免打印任何内容?

So I think it's because Perl is trying to write to a closed handle. How can I avoid anything being printed?

推荐答案

close $out并关闭其STDOUT时,您不会终止外部程序.

You don't terminate the external program when you close $out, you close its STDOUT.

要关闭程序,您需要获取其进程ID,然后向其发送适当的信号. open调用返回pid,只保存它即可.然后在满足条件时发送信号.

In order to close the program you need to get its process id and then send it a suitable signal. The open call returns the pid, just save it. Then send a signal when the condition is met.

use Scalar::Util qw(openhandle); # to check whether the handle is open

my $pid = open (my $out, "-|", "usfos $memory<input-$cpu.scr") 
                // die "Can't fork: $!";   # / stop editor red coloring

while (<$out>) {
    if (/MIN   STEP  LENGTH/) {
        kill "TERM", $pid;   # or whatever is appropriate; add checks
        close $out;          # no check: no process any more so returns false
        last;
    }
}
# Close it -- if it is still open. 
if (openhandle($out)) {          
    close $out or warn "Error closing pipe: $!";
}

请参阅 打开 opentut ,以及 关闭.

See open and opentut, and close.

Fortran的错误确实与写入不存在的控制台(STDOUT)有关,因此在我看来,您已正确诊断出该问题:当条件匹配时,您停止读取(last),然后关闭程序的STDOUT,它在下一次尝试写入时导致报告的错误.

Fortran's error is indeed about writing to a non-existent console (STDOUT), so it seems to me that you diagnosed the problem correctly: as the condition is matched, you stop reading (last) and then close the program's STDOUT, which causes the reported error on its next attempted write.

一些注意事项.管道上的close等待其他过程完成.如果另一端的程序有close已知的问题,则$?可能需要查询.如果在另一个程序完成写入之前关闭了管道,则在下一次写入该管道时,该程序将得到SIGPIPE.来自关闭 文档

A few notes. A close on the pipe waits for the other process to finish. If the program on the other end has a problem that is going to be known at close, so $? may need interrogation. If a pipe is closed before the other program is done writing on its next write to that pipe the program will get a SIGPIPE. From close documentation

如果文件句柄来自管道打开,如果所涉及的其他系统调用之一失败或它的程序以非零状态退出,则close返回false.如果唯一的问题是程序退出了非零值,则$!将设置为0.关闭管道还等待管道上执行的进程退出(以防您以后要查看管道的输出),并将该命令的退出状态值隐式地放入$?中.和$ {^ CHILD_ERROR_NATIVE}.
...
在写另一端的过程完成之前,先关闭管道的读端,然后在写入器中接收SIGPIPE的结果.如果另一端无法解决问题,请确保在关闭管道之前先读取所有数据.

If the filehandle came from a piped open, close returns false if one of the other syscalls involved fails or if its program exits with non-zero status. If the only problem was that the program exited non-zero, $! will be set to 0 . Closing a pipe also waits for the process executing on the pipe to exit--in case you wish to look at the output of the pipe afterwards--and implicitly puts the exit status value of that command into $? and ${^CHILD_ERROR_NATIVE} .
...
Closing the read end of a pipe before the process writing to it at the other end is done writing results in the writer receiving a SIGPIPE. If the other end can't handle that, be sure to read all the data before closing the pipe.


注意目的是在进程STDOUT连接到文件句柄时终止进程,并关闭该文件句柄(一旦满足条件).如果在进程可能仍在写时首先关闭句柄,则我们正在向可能不太了解的进程发送SIGPIPE. (它处理信号吗?)另一方面,如果我们首先终止它,则close($out)无法成功完成,因为$out所连接的过程已经消失了.


Note   The objective is to kill a process while its STDOUT is connected to a filehandle and to close that filehandle (once a condition is met). If we first close the handle while the process may still write, we are sending a SIGPIPE to a process we may not know much about. (Does it handle the signal?) On the other hand, if we first terminate it the close($out) cannot complete with success, since the process that $out was connected to is gone.

这就是为什么代码在进程终止后不检查从调用返回到close的原因,因为它为假(如果kill成功).在循环之后,它会在关闭它之前检查该句柄是否仍然处于打开状态,因为我们可能已经关闭它,或者可能尚未关闭它.软件包 Scalar :: Util 用于此,另一个选项是 fileno .请注意,该代码不会检查kill是否完成了该工作,请根据需要添加.

This is why the code does not check the return from the call to close after the process is killed, as it is false (if kill succeeded). After the loop it checks whether the handle is still open before closing it, as we may or may have not closed it already. Package Scalar::Util is used for this, another option being fileno. Note that the code doesn't check whether kill did the job, add that as needed.

在Windows系统上,这有点不同,因为您需要找到子进程的ID.您可以使用其名称来执行此操作,然后使用

On a Windows system, it is a little different since you need to find the process ID of the child. You can do this using its name and then terminate it either using Win32::Process::Kill, or using kill. (Or, see a Windows command that should do all this, below.) To find the process ID try either of

use Win32::Process::Kill;
use Win32::Process::List;
my $pobj = Win32::Process::List->new(); 
my %proc = $pobj->GetProcesses(); 
my $exitcode;
foreach my $pid (sort { $a <=> $b } keys %proc) {
    my $name = $proc{$pid};
    if ($name =~ /usfos\.exe/) {
        Win32::Process::KillProcess($pid, \$exitcode);
        # kill 21, $pid;
        last;
    }
 }

  • 使用 Win32 :: Process :: Info .参见此帖子.

  • Using Win32::Process::Info. See this post.

    use Win32::Process::Info;
    Win32::Process::Info->Set(variant=>'WMI');   # SEE DOCS about WMI
    my $pobj = Win32::Process::Info->new();
    foreach my $pid ($pobj->ListPids) {
        my ($info) = $pobj->GetProcInfo($pid);
        if ($info->{CommandLine} =~ /^usfso/) {  # command-line, not name
            my $proc = $info->{ProcessId};
            kill 2, $proc; 
            last;
        }
    }
    

    请注意,此模块还提供方法Subprocesses([$ppid,...]),该方法将标识提交的$ppid的所有子级.对于每个提交的$ppid,它都返回一个散列,该散列由$ppid s索引,并包含具有所有子进程$pid s的array-ref.

    Note that this module also provides the method Subprocesses([$ppid,...]), which will identify all children of the submitted $ppid(s). It returns a hash, which is indexed by $ppids and contains an array-ref with $pids of all subprocesses, for each $ppid submitted.

    use Win32::Process::Info;
    Win32::Process::Info->Set(variant=>'WMI');
    my $pobj = Win32::Process::Info->new();
    my %subproc = $pobj->Subprocesses([$pid]);   # pid returned by open() call
    my $rkids = $subproc{$pid};
    foreach my $kid (@$rkids) {
        print "pid: $kid\n";                     # I'd first check what is there
    }
    

  • 我遇到了Windows命令TASKKILL /T,该命令应终止进程及其子进程

  • I ran into a Windows command TASKKILL /T that should terminate a process and its children

    system("TASKKILL /F /T /PID $pid");
    

  • 我现在无法测试任何Windows代码.

    I cannot test any of Windows code right now.

    这篇关于Perl关闭管道没有错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆