【问题标题】:Perl: Grabbing the nth and mth delimited words from each line in a filePerl:从文件中的每一行中获取第 n 个和第 m 个分隔的单词
【发布时间】:2010-09-14 10:27:34
【问题描述】:

由于在 Nagios 中添加要监控的主机的方式更为繁琐(它需要定义一个主机对象,而不是之前只需要 IP 和主机名的程序),我认为最好自动执行此操作,这将是学习 Perl 的好时机,因为我目前只知道 C/C++ 和 Java。

我从中读取的文件如下所示:

xxx.xxx.xxx.xxx hostname #comments. i.dont. care. about

我想要的只是前 2 组字符。这些显然是用空格分隔的,但为了通用性,它也可以是任何东西。为了更笼统地说,为什么不是第一和第三,或第四和第十?当然肯定会涉及到一些正则表达式操作,但我暂时不考虑那个标签,以防万一。

【问题讨论】:

    标签: regex perl file-io


    【解决方案1】:

    如果您不编写更多 Perl 来处理结果,那么单行代码很棒。

    更一般地说,在较大的 Perl 程序的上下文中,您可以编写自定义正则表达式,例如:

    if($line =~ m/(\S+)\s+(\S+)/) {
         $ip = $1;
         $hostname = $2;
    }
    

    ... 或者您将使用 split 运算符。

    my @arr = split(/ /, $line);
    $ip = $arr[0];
    $hostname = $arr[1];
    

    无论哪种方式,添加逻辑以检查无效输入。

    【讨论】:

    • 我会说做列表分配更习惯:例如, ($ip, $hostname) = ($1, $2) 在第一种情况下,或 ($ip, $hostname) = (split ' ', $line)[0,1] 在第二个。 (0,1 以防万一人们想使用其他数字。如果不是, ($ip, $hostname) = split ' ',$line 就可以正常工作。
    • 不,更惯用的应该是“if (my ($ip,$hostname) = $line =~ /(\S+)\s+(\S+)/) {”。
    • 你们俩都是对的,但我不认为惯用 == 更好。
    【解决方案2】:

    让我们把它变成代码高尔夫!根据大卫的出色回答,这是我的:

    perl -ane 'print "@F[0,1]\n";'
    

    编辑:真正的高尔夫提交看起来更像这样(剃掉五杆):

    perl -ape '$_="@F[0,1]
    "'
    

    但这对于这个问题的目的来说不太可读。 :-P

    【讨论】:

    • 谢谢!我用更高尔夫球的东西修改了条目,但也可能更难以理解。 :-P
    【解决方案3】:

    这是一个通用的解决方案(如果我们稍微远离代码高尔夫球)。

    #!/usr/bin/perl -n
    chop;                     # strip newline (in case next line doesn't strip it)
    s/#.*//;                  # strip comments
    next unless /\S/;         # don't process line if it has nothing (left)
    @fields = (split)[0,1];   # split line, and get wanted fields
    print join(' ', @fields), "\n";
    

    通常split 以空格分隔。如果这不是您想要的(例如,解析 /etc/passwd),您可以将分隔符作为正则表达式传递:

    @fields = (split /:/)[0,2,4..6];
    

    当然,如果您正在解析以冒号分隔的文件,那么这些文件也很有可能没有 cmets,您也不必删除它们。

    【讨论】:

    • 你应该几乎总是使用 chomp 而不是chop。砍总是从字符串中删除最后一个字符。 chomp 从字符串中删除当前行终止符(通常为“\n”)(如果存在)。如果该行不以终止符结尾,则 chomp 什么也不做。印章可能会删除你不期望的东西。
    • Unix 方式是所有文本文件都以换行符结尾。因此,除非您的文件已填充,否则您永远不会读取末尾没有换行符的行。这对于像 /etc 中的文件来说是双倍的。 :-)
    • 只是好奇,chop 并没有特别提到任何东西。在这种情况下,您是否将文件传送到程序中?
    • Perl 有很多“隐含的东西”,以使程序简洁(Python 人讨厌这样,因此 Python 的规则是明确的)。默认情况下,chop 使用 $_,就像 split 一样,模式匹配也是如此。 [继续]
    • [续] -n 选项(见第 1 行)使 Perl 将行(如果没有参数,则从标准输入,否则从每个命名文件)读入 $_,整个程序实际上是在一个 while 循环中.这就是“下一个”语句(相当于 C 中的“继续”)起作用的原因。
    【解决方案4】:

    一个简单的单行是

    perl -nae 'print "$F[0] $F[1]\n";'
    

    您可以使用-F更改分隔符

    【讨论】:

      【解决方案5】:

      大卫尼姆说:

      perl -nae 'print "$F[0] $F[1}\n";
      

      使用-a 开关。我必须查一下:

      -a   turns on autosplit mode when used with a -n or -p.  An implicit split
           command to the @F array is done as the first thing inside the implicit
           while loop produced by the -n or -p.
      

      你每天都会学到一些东西。 -n 使每一行都传递给

      LINE:
          while (<>) {
              ...             # your program goes here
          }
      

      最后-e 是一种直接输入程序单行的方法。您可以拥有多个-e。其中大部分内容是对perlrun(1) 联机帮助页的抄袭。

      【讨论】:

      • “autosplit”模式也称为“awk模式”,使用@F作为数组名取自awk。
      【解决方案6】:

      既然 ray 问了,我想我会重写我的整个程序而不使用 Perl 的隐含性(除了使用 &lt;ARGV&gt;;这很难用手写出来)。这可能会让 Python 人更快乐(尽管有大括号 :-P):

      while (my $line = <ARGV>) {
          chop $line;
          $line =~ s/#.*//;
          next unless $line =~ /\S/;
          @fields = (split ' ', $line)[0,1];
          print join(' ', @fields), "\n";
      }
      

      有什么我错过的吗?希望不会。 ARGV 文件句柄是特殊的。它会读取命令行上的每个命名文件,除非未指定,否则它会读取标准输入。

      编辑:哦,我忘了。 split ' ' 也很神奇,不像 split / /。后者只是匹配一个空格。前者匹配任意数量的任意空格。如果没有为split 指定模式,则默认使用这种神奇的行为。 (有人会说,但是/\s+/呢?' '/\s+/ 是相似的,除了如何处理行首的空格。所以' ' 真的很神奇。 )

      这个故事的寓意是,如果你喜欢很多神奇的行为,Perl 就很棒。如果您没有它,请使用 Python。 :-P

      【讨论】:

      • 用chomp代替chop,以防文件的最后一行缺少换行符。
      • ysth:据我所知,这个问题与 Unix 有关,而 Unix 文本文件总是以换行符结尾。这对于应该在 /etc 中的文件来说是双倍的。
      • 多年来我一直虔诚地使用 chomp,但得出的结论是(出于多种目的)它是不必要的,这就是为什么首先要使用 chomp。
      【解决方案7】:

      在第 L 行中查找第 N 到第 M 个字符 --- 查找标签示例


      @echo off
      
      REM Next line = Set command value to a file  OR  Just Choose Your File By Skipping The Line
      vol E: > %temp%\justtmp.txt
      REM  Vol E:  = Find Volume Lable Of Drive E
      
      REM  Next Line to choose line line no. +0 = line no. 1 
      for /f "usebackq delims=" %%a in (`more +0 %temp%\justtmp.txt`) DO (set findstringline=%%a& goto :nextstep)
      
      :nextstep
      
      REM  Next line to read nth to mth Character  here 22th Character to 40th Character
      set result=%findstringline:~22,40%
      
      echo %result%
      pause
      exit /b
      

      另存为查找标签.cmd

      结果将是您的 Drive E 标签

      享受

      【讨论】:

        猜你喜欢
        • 2022-10-01
        • 2019-03-22
        • 2013-10-07
        • 1970-01-01
        • 2016-07-07
        • 2018-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多