【问题标题】:perl pgrep returning multiple pidsperl pgrep 返回多个 pid
【发布时间】:2015-08-01 01:27:17
【问题描述】:

我有一些这样的 perl 代码来获取进程的 pid,如果它 存在。我想知道 pgrep 的返回状态,以便确定 pid 是否有效。代码如下:

#!/usr/bin/perl -w
my $pid=`pgrep -n -f \"akonadiserver\"`;
print "$? ";
chomp $pid;
print "Output1: $pid\n";
$pid=`pgrep -n -f \"akonadiserver\"`;
my $evalue=${^CHILD_ERROR_NATIVE};
chomp $pid;
print "Output2: $pid, $evalue\n";

但是每当我运行它时,我都会得到以下信息:

0 Output1: 3054
Output2: 3056, 0

再跑一次

0 Output1: 3059
Output2: 3061, 0

但在系统中:

$ pgrep -n -f akonadiserver
1862

这里看到的几个问题:

1) 返回的 pid 一直在变化,很可能是匹配的 grep 进程本身。

2) 我没有办法找出返回的 pid 是否有效。 $? 没用。

如何检查返回状态以及如何在 perl 中正确使用 pgrep?

【问题讨论】:

  • "find out of the pid returned is valid or not" 是什么意思?来自documentation"pgrep looks through the currently running processes and lists the process IDs which match the selection criteria to stdout".. 所以任何返回的 PID 都将是一个正在运行的进程..
  • 如上例所述,本应为 pid 的返回值不正确。我得到 3056、3059 等,而进程的实际 pid 是 1862。所以当它返回不同的(错误的)pid 时,我如何检查状态?因为显然 $?不工作。
  • 您不能使用pgrep 的返回值来检查进程是否正确。返回值只是说明它是否找到任何进程。如果它找到一个、两个、三个或任何正数的进程,则返回值将为零。您需要做的是检查命令行中是否有某些东西(您认为是正确的进程)将其与不正确的进程分开,或者是否有其他一些特征(如开始时间)可以将正确的进程与其他进程分开..

标签: perl


【解决方案1】:

从 Perl 脚本运行 `pgrep -n -f akonadiserver` 相当于从终端运行 sh -c "pgrep -n -f akonadiserver"

来自perlop

qx/STRING/
一个(可能)被插值的字符串,然后作为系统命令使用 /bin/sh 或其等效命令执行。

问题是使用带有pgrep-f 选项将匹配用于调用进程的完整命令行。这也将包括当前的pgrep 命令,因为它的命令行看起来像sh -c "pgrep -n -f akonadiserver",它将匹配字符串"akonadiserver"。由于您还将-n 标志与pgrep 一起使用,它只会返回与"akonadiserver" 匹配的最新进程,这很可能是pgrep 命令本身(sh -c "pgrep -n -f akonadiserver")。所以这就解释了为什么你得到错误的进程 ID。

但它没有解释为什么它仍然可以从终端窗口工作。原因是在终端窗口中,pgrep 命令使用bash 而不是sh(来自Perl 脚本)运行,有关差异的更多信息,请参阅此问题:Difference between pgrep in sh and bash。因此,从终端窗口运行 pgrep -f 将不包含 pgrep 命令本身,因此它适用于您的情况。

要从 Perl 获得可靠的结果,您应该使用 Proc::ProcessTable

use feature qw(say);
use strict;
use warnings;

use Proc::ProcessTable;

my $pat = "akonadiserver";
my $pt = Proc::ProcessTable->new();
my @tab;
for my $p ( @{$pt->table} ){
    if ($p->cmndline =~ /\Q$pat\E/) {
        push @tab, {pid => $p->pid, start => $p->start};
    }
}

say $_->{pid} for (sort {$b->{start} <=> $a->{start}} @tab);

【讨论】:

  • 这很干净,解释也很到位。我即将以这个问题结束,但是如果没有 ProcessTable 有什么方法可以做到这一点,例如。只有 sed、awk 和 pgrep ?是否会像读取 pid 两次并验证它们在所有情况下是否相同工作?
  • 您的意思是从 Perl 脚本中调用 awk 吗?为什么要避开Proc::ProcessTable
  • awk 或 sed 或任何来自 perl 的东西,而无需安装 perl 模块。在嵌入式环境中,除了最低限度之外,可能无法 perl 模块。
  • 你可以试试perl -E '@a=grep {$_=~/akonadiserver/ &amp;&amp; $_!~/pgrep/} qx{ps -eo pid,cmd --sort=start_time}; say @{[split " ", $a[-1]]}[0]'
  • 这个再次返回 Grep 本身的 pid。 pastebin.com/d3kdPuCv 。不使用 ProcessTable 就不可能找到这个吗?
【解决方案2】:

在我看来,pgrep 返回由您正在寻找的进程启动的最新 (-n) 子进程的 pid,至少在我的系统上是这样。

这种事情我一直用ps,怎么样:

my $pid = (split(" ",`ps -A | grep \"akonadiserver\"`))[0];

改为,或

my @pids = map{ $_ =~ /(\d+)/; $1 }split("\n", `ps -A | grep \"akonadiserver\"` );
print "mutiple instances: @pids\n" if @pids > 1;
print "pid: ".$pids[0]."\n" if @pids == 1;

让所有实例运行到一个数组中,然后用它做你想做的事情。

【讨论】:

  • 在第一个解决方案中:[0];这是做什么的?如果它返回进程列表的第 0 个条目,想象一下如果进程没有运行会发生什么,它将返回 grep 进程的 pid,我回到第一方,我不知道返回的 pid 是否为正在运行的进程的pid或grep的pid。
  • 不,不会。该列表将是空的,因此空列表中的第一个元素是 undef,您最终会在 $pid 中找到 undef。你还想要什么?我正在使用您的 grep 标准。这就是为什么 ps -a | grep 而不仅仅是 ps -A。你甚至尝试过吗?另外,我非常怀疑第一个条目是 grep。
  • 当然,我试过了。 link。当您提供的进程未运行时,您为什么会怀疑第一个条目是否是 grep 。那么第一个条目将是 grep。
  • 我对此表示怀疑,因为我在我的系统上对其进行了测试,它运行良好,之前也运行良好,而您提供的链接仅表明您调用了一些 text.txt,它可能是任何东西。不过,上面的解释很有趣:)
猜你喜欢
  • 1970-01-01
  • 2017-08-25
  • 2019-01-13
  • 1970-01-01
  • 1970-01-01
  • 2017-06-08
  • 2013-01-28
  • 1970-01-01
  • 2021-02-07
相关资源
最近更新 更多