有人可以建议这个Perl脚本是如何工作的吗?

pav*_*ium -1 perl parsing

我必须维护以下Perl脚本:

#!/usr/bin/perl -w

die "Usage: $0 <file1> <file2>\n" unless scalar(@ARGV)>1;

undef $/;
my @f1 = split(/(?=(?:SERIAL NUMBER:\s+\d+))/, <>);
my @f2 = split(/(?=(?:SERIAL NUMBER:\s+\d+))/, <>);

die "Error: file1 has $#f1 serials, file2 has $#f2\n" if ($#f1 != $#f2);

foreach my $g (0 .. $#f1) {
    print (($f2[$g] =~ m/RESULT:\s+PASS/) ? $f2[$g] : $f1[$g]);
}

print STDERR "$#f1 serials found\n";
Run Code Online (Sandbox Code Playgroud)

我几乎知道它的作用,但是它的完成方式很难理解.对split()的调用特别令人费解.

这是相当惯用的Perl,如果Perl专家可以就如何做到这一点做出一些澄清建议,我将不胜感激,因此,如果我需要在输入文件上使用它,它无法处理,我可以尝试修改它.

它结合了包含测试结果的两个数据记录文件的最佳结果.数据记录文件包含各种序列号的结果,每个序列号的数据以序列号开头和结尾:n(我知道这是因为我的设备创建了输入文件)

我可以描述数据记录文件的格式,但我认为唯一重要的方面是SERIAL NUMBER:n 因为这是所有Perl脚本检查的

三元运算符用于从一个输入文件或另一个输入文件打印值,因此输出可以重定向到第三个文件.

Chr*_*utz 8

这可能不是我所谓的"惯用语"(那将是use Module::To::Do::Task),但他们肯定(ab)在这里使用了一些语言功能.我会看看我是否能为你揭开神秘面纱的神秘面纱.

die "Usage: $0 <file1> <file2>\n" unless scalar(@ARGV)>1;
Run Code Online (Sandbox Code Playgroud)

如果它们没有给我们任何参数,则退出时会显示一条用法消息.命令行参数存储在@ARGV其中,类似于C,char **argv除了第一个元素是第一个参数,而不是程序名称.scalar(@ARGV)转换@ARGV为"标量上下文",这意味着,虽然@ARGV通常是一个列表,但我们想知道它的标量(即非列表)属性.当列表转换为标量上下文时,我们得到列表的长度.因此,unless只有在我们不传递参数时才满​​足条件.

这是相当误导的,因为它会使你的程序需要两个参数.如果我写了这个,我会写:

die "Usage: $0 <file1> <file2>\n" unless @ARGV == 2;
Run Code Online (Sandbox Code Playgroud)

注意我离开了scalar(@ARGV),只是写了@ARGV.该scalar()函数强制标量上下文,但如果我们将相等性与数字进行比较,则Perl可以隐式地假设标量上下文.

undef $/;
Run Code Online (Sandbox Code Playgroud)

钱币.该$/变量是一个特殊的Perl内置变量,Perl用它来判断文件中的"行"数据是什么.通常,$/设置为字符串"\n",这意味着当Perl尝试读取一行时,它将读取直到下一个换行符(或Windows上的回车符/换行符).undef但是,你的编写者已经使用了变量,这意味着当你尝试读取"行"时,Perl只会淹没整个文件.

my @f1 = split(/(?=(?:SERIAL NUMBER:\s+\d+))/, <>);
Run Code Online (Sandbox Code Playgroud)

这是一个有趣的.<>是一个特殊的文件句柄,它从命令行上给出的每个文件中逐行读取.但是,由于我们已经告诉Perl"行"是一个整个文件,因此调用<>一次将读取第一个参数中给出的整个文件,并将其临时存储为字符串.

然后我们split()使用正则表达式将该字符串分成几部分/(?=(?:SERIAL NUMBER:\s+\d+))/.这使用了一个前瞻,它告诉我们的正则表达式引擎"只有匹配,如果这些东西我们的比赛后出现,但这些东西不是我们的比赛的一部分,"基本上允许我们先看看我们的比赛,以检查更多的信息.它基本上将文件分成多个部分,其中每个部分(可能除了第一个)开始"SERIAL NUMBER:",一些任意的空格(\s+部分),然后是一些数字(\d+部分).我不能教你正则表达式,所以更多信息我建议阅读perldoc perlretut - 他们会比我更好地解释所有这些.

一旦我们将字符串拆分成一个列表,我们就将该列表存储在一个名为的列表中@f1.

my @f2 = split(/(?=(?:SERIAL NUMBER:\s+\d+))/, <>);
Run Code Online (Sandbox Code Playgroud)

这与最后一行完全相同,仅与第二个文件相同,因为<>它已经读取了整个第一个文件,并将列表存储在另一个名为的变量中@f2.

die "Error: file1 has $#f1 serials, file2 has $#f2\n" if ($#f1 != $#f2);
Run Code Online (Sandbox Code Playgroud)

这条线打印如果错误消息@f1@f2具有不同的尺寸.$#f1是数组的特殊语法 - 它返回最后一个元素的索引,通常是列表的大小减去1(列表是0索引的,就像在大多数语言中一样).他在他的错误消息中使用了相同的值,这可能具有欺骗性,因为它将比预期的打印少1.我会把它写成:

die "Error: file $ARGV[0] has ", $#f1 + 1, " serials, file $ARGV[1] has ", $#f2 + 1, "\n"
  if $#f1 != $#f2;
Run Code Online (Sandbox Code Playgroud)

注意我将"file1"更改为"file $ ARGV [0]" - 这样,它将打印您指定的文件的名称,而不仅仅是模糊的"file1".另请注意,我将die()功能和if()条件分为两行.我觉得它更具可读性.我也可以写unless $#f1 == $#f2而不是if $#f1 != $#f2,但那只是因为我碰巧认为!=是一个丑陋的操作员.有不止一种方法可以做到这一点.

foreach my $g (0 .. $#f1) {
Run Code Online (Sandbox Code Playgroud)

这是Perl中常见的习语.我们通常使用for()foreach()(同样的,真的)迭代列表的每个元素.但是,有时我们需要该列表的索引(某些语言可能使用术语"枚举"),因此我们使用范围运算符(..)来创建一个列表,从列表0开始$#f1,即通过列表的所有索引,因为$#f1是我们列表中最高指数的值.Perl将循环遍历每个索引,并在每个循环中,将该索引的值分配给词法范围的变量$g(虽然为什么他们$i不像任何理智的程序员那样使用,我不知道 - 来吧,人们,这个自Fortran以来,传统一直存在!).所以第一次通过循环,$g将是0,第二次是1,依此类推,直到最后一次$#f1.

    print (($f2[$g] =~ m/RESULT:\s+PASS/) ? $f2[$g] : $f1[$g]);
Run Code Online (Sandbox Code Playgroud)

这是我们循环的主体,它使用三元条件运算符?:.三元运算符没有问题,但是如果代码给你带来麻烦,我们可以把它改成一个if().让我们继续并重写它以使用if():

    if($f2[$g] =~ m/RESULT:\s+PASS/) {
        print $f2[$g];
    } else {
        print $f1[$g];
    }
Run Code Online (Sandbox Code Playgroud)

非常简单 - 我们进行正则表达式检查$f2[$g](我们的第二个文件中的条目对应于我们的第一个文件中的当前条目),它基本上检查该测试是否通过.如果确实如此,我们打印$f2[$g](这将告诉我们测试通过),否则我们打印$f1[$g](这将告诉我们测试失败).

print STDERR "$#f1 serials found\n";
Run Code Online (Sandbox Code Playgroud)

这只是打印一个结束诊断消息,告诉我们找到了多少个连续出版物(再次减去一个).

我个人会重写那个整个毛茸茸的位置,$/然后做两个读取<>来成为一个循环,因为我认为这样会更具可读性,但是这段代码应该可以正常工作,所以如果你不需要改变它你应该保持良好状态.


JB.*_*JB. 6

undef $/行将取消激活输入记录分隔符.解释器将在此之后立即读取整个文件,而不是逐行读取记录.

<>,或"钻石经营者"从命令行或标准输入,无论是有道理的文件中读取.在您的情况下,命令行被明确检查,因此它将是文件.输入记录分隔已被停用,因此每次看到a时<>,您都可以将其视为将整个文件作为字符串返回的函数调用.

split运营商利用这个字符串,并把它切成块,每个块满足在争论正则表达式的时间.该(?= ... )结构的意思是"分隔符是这样的,但请保持它在分块结果呢."

这里的所有都是它的.总会有一些优化,简化或"其他方法",但这应该让你运行.