【问题标题】:How to determine if Java process is running in Perl如何确定 Java 进程是否在 Perl 中运行
【发布时间】:2012-09-17 13:24:29
【问题描述】:

我有一套小型 Java 应用程序,它们全部编译/打包为 <name-of-the-app>.jar 并在我的服务器上运行。偶尔其中一个会抛出异常,窒息而死。我正在尝试编写一个 quick-n-dirty Perl 脚本,该脚本将定期轮询以查看所有这些可执行 JAR 是否仍在运行,如果其中任何一个不在运行,请给我发送电子邮件并通知我哪个已死。

要手动确定这一点,我必须为要检查的每个应用程序运行 ps -aef | grep <name-of-app>。例如,要查看myapp.jar 是否作为进程运行,我运行ps -aef | grep myapp,并查找描述代表myapp.jar 的JVM 进程的grep 结果。这种手动检查现在变得乏味,是自动化的主要候选者!

我正在尝试实现检查进程是否正在运行的代码。我想让这个sub 接受可执行JAR 的名称并返回truefalse

sub isAppStillRunning($appName) {
    # Somehow run "ps -aef | grep $appName"

    # Somehow count the number of processes returned by the grep

    # Since grep always returns itself, determine if (count - 1) == 1.
    # If so, return true, otherwise, false.
}

我需要能够传递sub 应用程序的名称,运行我的正常命令,并计算grep 返回的结果数。由于运行grep 总是会产生至少一个结果(grep 命令本身),我需要逻辑来说明如果 (# of results - 1) 等于 1,那么我们知道应用程序正在运行。

我是 Perl 的新手,很难弄清楚如何实现这个逻辑。到目前为止,这是我最好的尝试:

sub isAppStillRunning($appName) {
    # Somehow run "ps -aef | grep $appName"
    @grepResults = `ps -aef | grep $appName`;

    # Somehow count the number of processes returned by the grep
    $grepResultCount = length(@grepResults);

    # Since grep always returns itself, determine if (count - 1) == 1.
    # If so, return true, otherwise, false.
    if(($grepResultCount - 1) == 1)
        true
    else
        false
}

然后要从同一个 Perl 脚本中调用该方法,我认为我只需运行:

&isAppStillRunning("myapp");

非常感谢您在定义 sub 并使用正确的应用名称调用它方面的任何帮助。提前致谢!

【问题讨论】:

  • Perl 可以做到这一点,但这是在重新发明轮子。有many sysadmin solutions 用于重启进程。
  • Re: grep 在进程表中匹配它的参数,通常的回避方法是grep some[a]pp 而不是grep someapp。此外,grep -c some[a]pp 可以方便地为您提供匹配数。
  • 补充一下@pilcrow 所说的,我过去做过的另一件事是grep process_name | grep -v grep,以避免在其输出中包含grep 进程。

标签: perl process grep ps


【解决方案1】:

使用 CPAN 中的 Proc::ProcessTable 模块会容易十亿倍。以下是它的外观示例:

use strict;
use warnings;
use Proc::ProcessTable;

...
sub isAppStillRunning { 
    my $appname = shift;
    my $pt = Proc::ProcessTable->new;
    my @procs = grep { $_->fname =~ /$appname/ } @{ $pt->table };

    if ( @procs ) { 
        # we've got at least one proc matching $appname. Hooray!
    } else { 
        # everybody panic!
    }
}

isAppStillRUnning( "myapp" );

需要记住的一些注意事项:

  • 打开strictwarnings。他们是你的朋友。
  • 您没有使用prototypes 指定子例程参数。 (Perl 中的原型做了一些完全不同的事情,这是您不想要的。)使用 shift@_ 数组中获取参数。
  • 不要使用&调用子程序;就用它的名字吧。
  • 在标量上下文中评估的数组(包括如果它在if 内)会给出它的大小。 length 不适用于数组。

【讨论】:

    【解决方案2】:

    你可以像这样修改你的例程:

    sub isAppRunning {
        my $appName = shift;
        @grepResults = `ps -aef | grep $appName`;
        my $items = 0;
        for $item(@grepResults){
            $items++;
        }
        return $items;
    }
    

    这将遍历 @grepResults 并允许您在必要时检查 $item。

    这样调用它应该返回进程数:

    print(isAppRunning('myapp') . "\n"); 
    

    【讨论】:

    • 感谢@jcern (+1) - 3 个快速跟进:(1) 你能解释一下第一行 my @appName = shift 吗? shift 让我失望了!! (2) for 循环主体周围的花括号是强制性的,还是只是一种好的做法?即使只有 1 条语句,也不确定 Perl 是否强制使用大括号。 (3) Perl 不支持布尔真/假值吗?我想知道为什么你让 sub 返回项目计数而不是 true/false 是全部。再次感谢!
    • grep $appName 作为子进程运行会将 grep 进程包含在计数中。捕获ps -aef 并在 perl 中对结果进行 grep 会更准确。
    • @4herpsand7derpsago (2) 大括号几乎总是强制性的,因为 Perl 是根据块定义的,而不是语句,并且块由大括号分隔。 (3) 查看我对您问题的回答
    • 对于第1点,你可以这样想,函数的参数是数组的形式。您可以从该数组中弹出项目(或移动它们)并将它们分配给一个变量。对于 (2),通常需要大括号。并且(3)似乎在 amon 的回答中得到了很好的回答。
    【解决方案3】:

    你的 sub 差不多了,但最后的 if-else 结构必须更正,在某些情况下,Perl 习语可以让你的生活更轻松。

    Perl 有原型,但它们很烂

    sub isAppStillRunning($appName) {
    

    不会工作。而是使用

    sub isAppStillRunning {
      my ($appName) = @_;
    

    @_ 数组保存函数的参数。 Perl 有一些简单的原型(sub name(&$@) {...} 语法),但它们已损坏,而且是一个高级主题,所以不要使用它们。

    Perl 有内置的 Grep

    `ps -aef | grep $appName`;
    

    这将返回一 (1) 个字符串,可能包含多行。您可以在换行符处拆分输出,然后手动 grep,这比插值变量更安全:

    my @lines   = split /\n/ `ps -aef`;
    my @grepped = grep /$appName/, @lines;
    

    您还可以使用open 函数显式打开到ps 的管道:

    my @grepped = ();
    open my $ps, '-|', 'ps -aef' or die "can't invocate ps";
    while (<$ps>) {
      push @grepped if /$appName/;
    }
    

    这完全一样,但风格更好。它从ps 输出中读取所有行,然后将带有$appName 的所有行推送到@grepped 数组中。

    标量与列表上下文

    Perl 有一个不寻常的东西叫做“上下文”。有列表上下文标量上下文。例如,子例程调用采用参数列表 - 因此这些列表(通常)具有列表上下文。相反,连接两个字符串是一个标量上下文。

    数组的行为因上下文而异。在 list context 中,它们评估其元素,但在 scalar context 中,它们评估其元素的数量。因此无需手动计算元素(或使用适用于字符串的length 函数)。

    所以我们有:

     my @array = (1, 2, 3);
     print "Length: ", scalar(@array), "\n"; # prints "Length: 3\n"
     print "Elems: ", @array, "\n";          # prints "Elems: 123\n";
     print "LastIdx: ", $#array, "\n";       # prints "LastIdx: 2\n";
    

    最后一个形式,$#array,是数组中的最后一个索引。除非你干预特殊变量,否则这与@array - 1 相同。

    scalar 函数强制标量上下文。

    Perl 没有布尔值

    Perl 没有布尔数据类型,因此没有truefalse 关键字。相反,除非另有说明,否则所有值都是正确的。错误值为:

    空字符串""、数字零0、零字符串"0"、特殊值undef,以及其他一些你不会遇到的怪事。

    所以一般以1为真,0为假。

    if/else 结构需要花括号

    所以你的意思可能是:

    if (TEST) {
      return 1;
    } else {
      return 0;
    }
    

    return TEST 相同,其中 TEST 是一个条件。

    终极还原

    使用这些技巧,你的 sub 可以写得这么短

    sub isAppStillRunning {
       return scalar grep /$_[0]/, (split /\n/, `ps -aef`);
    }
    

    这将返回包含您的应用名称的行数。

    【讨论】:

    • 哇!谢谢@amon (+1) - 很好的指点!所以,我假设我会调用 sub 并通过调用 if(isAppStillRunning('myapp') == 1) 来检查应用程序是否正在运行?如果返回值为 1,我们就知道我们有一个匹配项,对吧?再次感谢!
    • @4herpsand7derpsago 不,返回值并不总是1。将返回值视为真或假值,即if (isAppStillRunning("yourapp")),无需进一步条件。
    • 啊,我明白了。但如果“myapp”没有运行,我总能指望它为 0,对吧?
    • 对不起@amon,最后一个问题!如果我想检查“myapp”是否没有运行,如果 Perl 不支持布尔值,我将如何调用 isAppStillRunning?我可以使用if(!isAppStillRunning("myapp")),还是必须更改 sub 内部的逻辑?
    • 关于检查否定条件,Perl 中一个优雅的选项是unless (isAppStillRunning("myapp")) { ... }。这与if (!...) 完全相同。
    猜你喜欢
    • 2010-10-26
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 2023-03-09
    • 2013-11-29
    相关资源
    最近更新 更多