了解 Perl one-liner

Understanding a Perl one-liner

我正在寻找一种方法来检查 AIX 上进程的内存使用情况并找到 this page,列出此命令:

# svmon -Pt15 | perl -e 'while(<>){print if($.==2||$&&&!$s++);$.=0 if(/^-+$/)}'

它对我来说工作得很好,但我想了解 Perl one-liner 部分是如何工作的。

我知道它正在解析 svmon 命令的输出。我已经理解了输出第二行的 $.==2 部分。 $.=0 部分重置了行号,因此它可以对 svmon.

列出的每个进程执行相同的处理

然而,我对||$&&&!$s++ 部分感到茫然。有一个或。 $& 是匹配的部分(什么?),&& 是 AND 运算符,但我也不确定我是否正确分解了它。

svmon returns(没有任何重定向)每个进程类似的行块。第一行类似于:

# svmon -Pt15 | head -n 20

-------------------------------------------------------------------------------
     Pid Command          Inuse      Pin     Pgsp  Virtual 64-bit Mthrd  16MB
12058652 java            579432     8261   397386   824106      Y     Y     N

     PageSize                Inuse        Pin       Pgsp    Virtual
     s    4 KB               67560        309       1610      40138
     m   64 KB               31992        497      24736      48998

    Vsid      Esid Type Description              PSize  Inuse   Pin Pgsp Virtual
  c86b4c        7e work text data BSS heap           m   4096     0   43    4096
  c30d43        7f work text data BSS heap           m   4096     0  215    4096
  db2358        68 work text data BSS heap           m   4089     0 3667    4096
  d25056        69 work text data BSS heap           m   4057     0  585    4096
  99d59b      1002 work text data BSS heap           m   3461     0 2061    4082
  b531b1        7d work text data BSS heap           m   3440     0   39    3440
  a551a7      1001 work text data BSS heap           m   2933     0 2597    3767
  970017  90000000 work shared library text          m   2172     0  213    2413
  ca3c48        6a work text data BSS heap           m   2090     0 2006    4096
  ade32e         4 work text or shared-lib code seg sm  25389     0    0   25389
... (tons of lines)
-------------------------------------------------------------------------------
     Pid Command          Inuse      Pin     Pgsp  Virtual 64-bit Mthrd  16MB
 6094910 java            494585     8110     5754   484444      Y     Y     N

     PageSize                Inuse        Pin       Pgsp    Virtual
     s    4 KB               31257        158       1610      16780
     m   64 KB               28958        497        259      29229

    Vsid      Esid Type Description              PSize  Inuse   Pin Pgsp Virtual
  a31ba7         8 work text or shared-lib code seg  m   4096     0    0    4096
  da3159         6 work text or shared-lib code seg  m   4096     0    0    4096
... repeated several times

Perl 部分的处理输出 returns headers,虚线和每个进程一行,以及命令和内存详细信息:

# svmon -Pt15 | perl -e 'while(<>){print if($.==2||$&&&!$s++);$.=0 if(/^-+$/)}'
-------------------------------------------------------------------------------
     Pid Command          Inuse      Pin     Pgsp  Virtual 64-bit Mthrd  16MB
12058652 java            579438     8261   397386   824106      Y     Y     N
 6094910 java            494583     8110     5754   484444      Y     Y     N
 5046378 java            382458     8217     5738   339847      Y     Y     N
21102818 java            352534     8149     5738   305644      Y     Y     N
18219048 java            321394     8081   176586   340617      Y     Y     N
18612404 java            235323     8161   100746   267565      Y     Y     N
 3735554 java            195412     8118   125306   222885      Y     Y     N
24772644 java            185403     8209    88474   202652      Y     Y     N
25559102 java            143341     8095     5738   118094      Y     Y     N
11272240 java            137082     8193    82810   167151      Y     Y     N
18874550 java            131531     8129    79898   144249      Y     Y     N
 5505082 java            121320     8075    50922   136195      Y     Y     N

您正在正确分解它,我们可以用 B::Deparse:

来验证
perl -MO=Deparse,-p -e 'while(<>){print if($.==2||$&&&!$s++);$.=0 if(/^-+$/)}'
while (defined(($_ = <ARGV>))) {
    ((($. == 2) || ($& && (!($s++)))) and print($_));
    (/^-+$/ and ($. = 0));
}

$& 包含最后一个匹配项,在这种情况下只有一个地方发生匹配:/^-+$/.

!$s++ 仅在 $s++ 为假时为真,即在第一个匹配项 ($& && !$s++).

perl -e 'while(<>){print if($.==2||$&&&!$s++);$.=0 if(/^-+$/)}'

乍一看,这看起来像是每隔一行打印一次,也是 --- 行之后的第一行。让我们看看零件。

while(<>) { ... } 以逐行模式读取标准输入(或文件,如果您提供参数)。在单行中执行此操作的最常见方法是使用 -n-p 开关。 IE。这个:

perl -e 'while(<>) { ... }'

与此相同:

perl -ne ' ... '
如果后面的条件为真,

print if 将打印 $_,其中一个读取行。条件if ( $. == 2 || $& && !$s++ )是一块笨重的马粪。让我们剖析它。

首先,让我们注意它由两个语句组成,用 || 连接,这意味着如果其中一个语句为真,则整个语句为真。其次,请注意它是短路的,这意味着如果左边的语句为真,则永远不会评估右边的语句。这是相关的,因为计数器变量 $s.

$. 是最后访问的输入文件句柄上行号的内置变量。它可以被操纵,但天知道人们为什么想要这样做。在这种情况下,如果行号为 2,则打印该行。

如果$.不是2,检查右边的语句。它也是一个链式语句,带有 &&,意味着两者都必须为真。实际上,所有三个语句都相互链接:a || b && c,这使得变量 return 和值 右侧的 operator precedence. Luckily (?), it seems to mean what we assume it means: (a || (b && c)). So, if $& is true, and !$s++ is true, the print goes through. The increment operator 变得相当复杂在 递增之前,在这种情况下,它 returns 0 第一次代码是 运行,取反变成 1,一个真值。此后的所有时间里,!$s++ return 都是假的。这是一个常见的 Perl 习语 return 仅对第一个遇到的值为真。

呸!

如果有一行仅由破折号 ---- 组成,则之后的部分会将 $. 重置为 0。大概这样做是为了分隔"records",例如:

foo
bar
------
next
...

但这引出了一个问题,为什么要 !$s++?我们总是打印每条记录的第二行吗?我们总是打印第一行 "not line #2",前面是一行 "just" 破折号 ----

所以让我们尝试总结一下...这将打印

  • 行号2在输入中,or第2行后一行只有破折号----if有中间没有只有破折号的线条。
  • 它找到的第一行破折号之后的第一行(而且只有第一条破折号)

Can the same result be done in a simpler, more robust way?

哦,是的,当然。这是做同样事情的例子,但使用更简单的工具:

$ perl -ne'if ($. <= 3) { print } elsif (/^-+$/) { <>; print <>.""; }'
-------------------------------------------------------------------------------
     Pid Command          Inuse      Pin     Pgsp  Virtual 64-bit Mthrd  16MB
12058652 java            579432     8261   397386   824106      Y     Y     N
 6094910 java            494585     8110     5754   484444      Y     Y     N
99058652 java            579432     8261   397386   824106      Y     Y     N
22058652 java            579432     8261   397386   824106      Y     Y     N

这基本上读起来像英文,这总是很好。它获取前三行并打印它们,然后检查 "dash line",丢弃其后的行,并打印下一行。我使用连接运算符 . 将文件句柄置于标量上下文中,以便仅打印一行。另一种方法是使用标量变量并打印它。

这是最好的方法吗?嗯...这是一个简单的方法。你可以通过创建一个程序文件来改进它,这将是 运行 比如:

svmon -Pt15 | perl program.pl

将它与 linux 系统的便利功能相结合,您将拥有一个新的命令供您使用。

那个程序可能是这样的:

use strict;
use warnings;

print <> . "" for 1 .. 3;

while (<>) {
    if (/^-+$/) {
        <>;
        print <> . "";
    }
}