似乎命令$sendmail 没有直接启动sendmail 程序,所以open 返回的$pid 不是sendmail 的(而是shell 的?)。
找到sendmail 进程本身的PID,kill 应该可以工作。 (或者考虑杀死整个进程组,见最后)。
您可以使用Proc::ProcessTable,而不是手动解析ps
use Proc::ProcessTable;
my $pid = open my $sm_fh, '|-', $sendmail or die "Can't open sendmail: $!";
my $pid_sm;
my $pt = Proc::ProcessTable->new();
foreach my $proc (@{$pt->table}) {
if ($proc->cmndline =~ /^sendmail/) { # adjust regex for your system
$pid_sm = $proc->pid;
say "Sendmail command-line: ", $proc->cmndline;
say "Sendmail process pid: ", $proc->pid;
}
}
kill 9, $pid_sm;
my $gone_pid = waitpid $pid_sm, 0;
say "Process $gone_pid is gone";
# need a handler for SIGPIPE for prints to $sm_fh further in code
在我的系统上,CMD 字段以sendmail 开头,请根据您的系统进行调整。如果可能有多个sendmail 进程,这很有可能,您需要进行更彻底的分析。
由于您需要将其从水中吹出,因此我认为无法修改其后续打印以进行检查。 (否则你可以用更简洁的方式解决这个问题。)
那么您必须为SIGPIPE 安装一个信号处理程序,否则程序将在下一次尝试打印到该文件句柄时死掉,因为它会得到一个SIGPIPE 并且它的处置是终止.
另一种解决方案是将sendmail 处理包装在Expect 中,这会设置一个伪终端,以便您可以在需要时发送Ctrl-C。 (它自己的 hard_close 方法也在我的测试中完成了这项工作。)但是为此应该修改打印语句,因此这里可能不可行。
更多细节。明确命令为:/usr/lib/sendmail -f$sender -t
模块的对象(上面的$pt)有很多进程表字段,由$pt->fields列出,描述在它的"stub module"。我发现打印和查看所有感兴趣的对象会提供更多信息。 exec、cwd 和各种ids 可能对此有所帮助。
如何准确识别进程取决于系统的详细信息,但一种方法是查看命令行详细信息。
上面的例子扩展了一点
$SIG{PIPE} = sub { print "Got $_[0]\n" }; # or just $SIG{PIPE} = 'IGNORE';
my $pid_sm;
foreach my $proc (@{$pt->table}) {
my $cmd = $proc->cmndline;
next if $cmd !~ m{^/usr/lib/sendmail};
if ( (split ' ', $cmd)[1] eq "-f$sender" ) {
$pid_sm = $proc->pid;
say "Our process $pid_sm: $cmd";
}
else { say "Some other sendmail: $cmd" }
}
warn "Didn't find our sendmail process" if not $pid_sm;
if ($needToAbort and $pid_sm) {
kill 9, $pid_sm;
my $gone_pid = waitpid $pid_sm, 0;
if ($gone_pid == -1) { say "No process $pid_sm" }
elsif ($gone_pid == 0) { say "Process $pid_sm still around?" }
else { say "Process $gone_pid is gone" }
};
命令行的第二个字段将根据确切的短语"-f$sender" 进行检查,这可以通过使用正则表达式而不是eq 来表示。查看为上述所有进程打印的命令行并根据需要进行调整。如果有问题,打印出任何包含'sendmail' 的内容。
另一种选择是杀死process group:kill 9, -$pid(注意减号)。这应该会捕获sendmail 进程本身,但当然要确保你知道什么被吹走了。
补充一点,我怀疑您是否需要使用SIGKILL (9)。一旦找到正确的 pid,SIGTERM(在我的系统上为 15,请参阅 man 7 signal)可能就足够了,还有什么更好。
最后,进程可能会被操作系统束缚并处于不可中断状态,尤其是在某些 I/O 操作中。但是,这似乎不太可能,我首先尝试上述两种方法。