【问题标题】:How to use split function in perl to do the work of awk如何在perl中使用split函数来完成awk的工作
【发布时间】:2017-01-19 15:15:57
【问题描述】:

我正在用 perl 编写脚本,我想尽可能避免使用 awk、tr 等外部命令。 这是我从脚本内部运行的命令,使用反引号:

my @arr = `$cmd | tr " " "\n" | xargs -n1 host | awk '{print $4}'`

这是没有awk '{print 4$}的输出:

$fileserver has address 10.90.207.59
$fileserver has address 10.90.207.34
$fileserver has address 10.90.207.56
$fileserver has address 10.90.207.67

这是awk '{print 4$}的输出:

10.90.207.59
10.90.207.34
10.90.207.56
10.90.207.67

我希望每个 IP 都位于数组中的单独单元格中。 如何使用 perl 函数(可能是拆分或映射)而不是使用 awk 和 tr?

【问题讨论】:

  • 输入和期望的输出?
  • 地址总是 IPv4 吗?

标签: regex perl awk


【解决方案1】:

琐碎。默认情况下splitawk 一样工作。所以:

my @arr = split;
print $arr[3]; #note - arrays start at zero. 

然而,perl 通常也可以在文件句柄上逐行运行,split 会得到你不想要的东西。

您可以:

#!/usr/bin/perl
use strict;
use warnings;

while (<DATA>) {
   my ($ip) = (split)[3];
   print $ip, "\n";
   #or push it. 
}

__DATA__
$fileserver has address 10.90.207.59
$fileserver has address 10.90.207.34
$fileserver has address 10.90.207.56
$fileserver has address 10.90.207.67

但如果你正在寻找一个单一的东西:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @ips = map { (split)[3] } <DATA>;

print Dumper \@ips;


__DATA__
$fileserver has address 10.90.207.59
$fileserver has address 10.90.207.34
$fileserver has address 10.90.207.56
$fileserver has address 10.90.207.67

我们在列表上下文中读取&lt;DATA&gt;,因此它返回整个内容 - 一次映射一个元素。

然后在map 中,我们拆分每个元素,并抓取元素3(在awk 术语中为$4)。

【讨论】:

  • 太好了,谢谢! ,关于“tr”命令我能做些什么吗?
  • 是的。但我不会,因为 那个 位正在使用xargs 运行多个命令,而我只是在perl 中一次运行一个命令。
【解决方案2】:

perl one liner 类似用途

perl -nae 'print "$F[3]\n"' input.txt

-n 为文件创建循环

-a 自动拆分模式。默认情况下用空格分割。存储到@F 中的输出。所以我使用第三个索引来打印 IP 地址。在 perl 索引中以 0 开始

【讨论】:

  • 也非常感谢!
【解决方案3】:

如果你知道你想要的字段是最后一列,你可以使用从末尾倒数的索引:

my $ip = (split)[-1];

在单行情况下,使用-a 开关使Perl 将行拆分为@F 数组(-nwhile(&lt;&gt;){...} 包裹在-e 的参数周围,-l 添加一个每个print 的换行符(见perlrun)):

perl -anle 'print $F[-1]'

但是,由于您没有被 awk 困住,因此您不必那样做。在没有管道的情况下,您可能可以在 Perl 中完成大部分工作。这是你开始的:

my @arr = `$cmd | tr " " "\n" | xargs -n1 host | awk '{print $4}'`

看起来$cmd 在一行中生成了一堆主机名。使用tr 将空格转换为换行符,然后在每一行上运行host。对于每个host 输出,您获取地址。

你可以在一个 Perl 程序中做到这一点:

 use v5.24;

 use Socket;        # core module
 use Net::hostent;  # core module

 my $cmd = ...;
 foreach my $host ( `$cmd` ) {
      chomp( $host );
      my @addresses = 
          map { inet_ntoa($_) } 
          gethostbyname($host)->addr_list->@*;
      say join "\n", @addresses;
     }

使用列表上下文中的反引号,Perl 将命令的输出分成几行。核心模块SocketNet::hostent 随Perl 一起提供。

我使用 v5.24 是因为它非常棒的 postfix dereferencing 功能 -&gt;@*,它将来自 addr_list 的数组引用转换为 map 可以使用的常规列表。

您需要小心您在$cmd 中输入的任何内容。我在Mastering Perl 的“安全”一章中详细讨论了这一点。您还可以在 perlsec 文档中找到一些内容。

【讨论】:

  • 非常感谢您的详细解答!
【解决方案4】:

Socket 模块中的inet_atoninet_ntoa 的帮助下,您可以非常简单地在Perl 中完成所有工作,$cmd 除外,因为您没有告诉我们那是什么

看起来您的$cmd 返回一行或多行主机名,以空格分隔,因此我在这里使用了echo 命令来模拟它。我也使用了Data::Dump,只是为了揭示@addresses的最终内容

use strict;
use warnings 'all';

use Socket;

my $cmd = 'echo www.amazon.co.uk www.perl.com www.stackoverflow.com';

my $cmd_output = `$cmd`;
my @addresses = map { name_to_ip($_) } split ' ', $cmd_output;

use Data::Dump;
dd \@addresses;

sub name_to_ip {

    my ($name) = @_;
    my $add32 = inet_aton($name) or die qq{Unable to convert host name "$name": $!\n};

    inet_ntoa($add32);
}

输出

["54.239.36.155", "207.171.7.72", "151.101.193.69"]

【讨论】:

    【解决方案5】:

    perl oneliners 来救援 - 列出我的出租车收据,按第 4 列排序,带有 _token delim:

        ls -1 | sort -t'_' -k 4| grep -i taxi
        00016,70_2021-12-04_OL_2021-12-04_id-10_LahiTaxi-Oy_TaxiKuljetus.jpg
        00125,10_2021-12-04_OL_2021-12-04_id-11_Tom-Lindroos_Taxipalvelut.jpg
    

    将它们视为一个表(对于控制台中的每一行,抓取到 @a 数组(这可能更短...),而不是通过它的索引来引用数组的每个元素:

    ls -1 | sort -t'_' -k 4|perl -ne 'chomp($_);@a=split/_/;print "$a[4]\t$a[3]\t$a[2]\t$a[1]\t$a[5]\t$a[6]\t$a[0]\n"' | grep -i taxi
    
        id-10   2021-12-04      OL      2021-12-04      LahiTaxi-Oy     TaxiKuljetus.jpg        00016,70
        id-11   2021-12-04      OL      2021-12-04      Tom-Lindroos    Taxipalvelut.jpg        00125,10
    

    将它们放入 csv 文件中,以导入到 xls、google sheet:

      ls -1 | sort -t'_' -k 4|perl -ne 'chomp($_);@a=split/_/;print "$a[4];$a[3];$a[2];$a[1];$a[5];$a[6];$a[0]\n"' > ../lst.csv
    

    【讨论】:

      猜你喜欢
      • 2013-12-03
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 1970-01-01
      • 2019-11-25
      • 2015-04-25
      • 1970-01-01
      • 2021-11-27
      相关资源
      最近更新 更多