如何在perl中使用split函数来完成awk的工作
Posted
技术标签:
【中文标题】如何在perl中使用split函数来完成awk的工作【英文标题】:How to use split function in perl to do the work of 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 吗? 【参考方案1】:琐碎。默认情况下split
与awk
一样工作。所以:
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
我们在列表上下文中读取<DATA>
,因此它返回整个内容 - 一次映射一个元素。
然后在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
数组(-n
将while(<>)...
包裹在-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 将命令的输出分成几行。核心模块Socket 和Net::hostent 随Perl 一起提供。
我使用 v5.24 是因为它非常棒的 postfix dereferencing 功能 ->@*
,它将来自 addr_list
的数组引用转换为 map
可以使用的常规列表。
您需要小心您在$cmd
中输入的任何内容。我在Mastering Perl 的“安全”一章中详细讨论了这一点。您还可以在 perlsec 文档中找到一些内容。
【讨论】:
非常感谢您的详细解答!【参考方案4】:在Socket
模块中的inet_aton
和inet_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.***.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 qqUnable 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
【讨论】:
以上是关于如何在perl中使用split函数来完成awk的工作的主要内容,如果未能解决你的问题,请参考以下文章
如何将数组的值作为第二个参数传递给 awk 的 split 函数?
linux下awk内置函数的使用(split/substr/length)