我正在使用Perl执行外部程序,并且如果在运行时返回特定字符串,则希望结束其执行。下面的代码根据需要中断执行,但是在执行最后一行(关闭)时会返回错误消息。
open (my $out, "-|", "usfos $memory<input-$cpu.scr");
while (<$out>) {
if ($_ =~ /MIN STEP LENGTH/) {
last;
}
}
close $out;
Run Code Online (Sandbox Code Playgroud)
这是所显示的错误的一部分(外部程序还会返回错误消息):
...forrtl: The pipe is being closed.
forrtl: severe (38): error during write, unit 6, file CONOUT$
Run Code Online (Sandbox Code Playgroud)
所以我认为这是因为Perl试图写一个封闭的句柄。如何避免打印任何内容?
当close $out您关闭外部程序时,您不会终止它STDOUT。
为了关闭程序,您需要获取其进程ID,然后向其发送适当的信号。该open调用返回的PID,只是保存。然后在满足条件时发送信号。
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: $!";
}
Run Code Online (Sandbox Code Playgroud)
Fortran的错误确实与写入不存在的控制台(STDOUT)有关,因此在我看来,您已正确诊断了该问题:当条件匹配时,您停止读取(last),然后关闭程序的STDOUT,这将导致报告错误。下一次尝试写入。
一些注意事项。close管道上的A 等待其他过程完成。如果另一端的程序有打算在被称为一个问题close,所以$?可能需要询问。如果在另一个程序完成写入之前关闭了管道,则在下一次写入该管道时,该程序将得到一个SIGPIPE。从近
文档
如果文件句柄来自管道打开,则如果所涉及的其他系统调用之一失败或它的程序以非零状态退出,则close返回false。如果唯一的问题是程序退出了非零值,则$!将设置为0。关闭管道还等待管道上执行的进程退出(以防您以后要查看管道的输出),并将该命令的退出状态值隐式地放入$?。和$ {^ CHILD_ERROR_NATIVE}。
...
在完成另一端的写入过程之前,先关闭管道的读取端,然后在写入器中接收到SIGPIPE的结果。如果另一端无法处理,请确保在关闭管道之前先读取所有数据。
注意 目标是在将进程STDOUT连接到文件句柄时将其杀死,并关闭该文件句柄(一旦满足条件)。如果我们在进程可能仍在写时先关闭句柄,那么我们正在向SIGPIPE可能不太了解的进程发送a 。(它处理信号吗?)另一方面,如果我们首先终止它close($out),则由于$out连接的过程已消失,因此无法成功完成。
这就是为什么代码close在进程终止后不检查从调用返回的原因,因为它是假的(如果kill成功)。循环之后,它会在关闭它之前检查该句柄是否仍然处于打开状态,因为我们可能已经关闭它,也可能尚未关闭它。软件包Scalar :: Util用于此目的,另一个选项是fileno。请注意,该代码不会检查kill该作业是否完成,请根据需要添加。
在Windows系统上,这有点不同,因为您需要找到子进程的ID。您可以使用其名称来执行此操作,然后使用Win32 :: Process :: Kill或使用终止它kill。(或者,请参阅下面的Windows命令,执行所有这些操作。)要查找进程ID,请尝试以下任一方法
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;
}
}
Run Code Online (Sandbox Code Playgroud)使用Win32 :: Process :: Info。看到这篇文章。
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;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,此模块还提供了方法Subprocesses([$ppid,...]),该方法将标识提交的所有子级$ppid。对于每个提交的对象,它返回一个哈希值,该哈希值由$ppids 索引,并包含$pid所有子进程的s 数组引用$ppid。
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
}
Run Code Online (Sandbox Code Playgroud)我遇到了Windows命令TASKKILL /T,该命令应终止进程及其子进程
system("TASKKILL /F /T /PID $pid");
Run Code Online (Sandbox Code Playgroud)我现在无法测试任何Windows代码。