我搜索了一下,希望我不会复制某人已经问过的内容。我有什么相当于特定格式的CSV(供应商要求)。有四个值分隔如下:
"Name","Description","Tag","IPAddresses"
列表很长(并且有大约150个唯一名称 - 下面的示例中只有2个)但它基本上是这样的:
"2B_AppName-Environment","desc","tag","192.168.1.1"
"2B_AppName-Environment","desc","tag","192.168.22.155"
"2B_AppName-Environment","desc","tag","10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4"
"6G_ServerName-AltEnv","desc","tag","192.192.192.40"
"6G_ServerName-AltEnv","desc","tag","192.168.50.5"
我希望在Perl(或sed / awk等)中提供以下方法:
"2B_AppName-Environment","desc","tag","192.168.1.1,192.168.22.155,10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4,192.192.192.40,192.168.50.5"
所以基本上,生成的文件会将重复项附加到第一个匹配项 - 每个应用程序/服务器名称只应该有一行,其中包含逗号分隔的IP地址列表,就像上面显示的那样。
请注意" Decription"和#34;标记"在复制删除/附加逻辑中不需要考虑这些字段 - 让我们假设这些字段是空白的,以便使事情变得更容易。此外,在供应商提供的列表中,"名称"所有条目都已经排序在一起。
答案 0 :(得分:1)
这个简短的Perl程序应该适合你。它期望输入CSV文件的路径作为命令行上的参数,并将结果打印到STDOUT
。它会跟踪name
数组中新@names
字段的外观,以便它可以按照每个名称首次出现的顺序打印输出,并获取desc
和tag
的值来自每个唯一name
的第一个出现的use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV->new({always_quote => 1, eol => "\n"});
my (@names, %data);
while (my $row = $csv->getline(*ARGV)) {
my $name = $row->[0];
if ($data{$name}) {
$data{$name}[3] .= ','.$row->[3];
}
else {
push @names, $name;
$data{$name} = $row;
}
}
for my $name (@names) {
$csv->print(*STDOUT, $data{$name});
}
。
"2B_AppName-Environment","desc","tag","192.168.1.1,192.168.22.155,10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4,192.192.192.40,192.168.50.5"
<强>输出强>
use strict;
use warnings;
use Text::CSV;
use Regexp::Common;
my $csv = Text::CSV->new({always_quote => 1, eol => "\n"});
my (@names, %data);
while (my $row = $csv->getline(*ARGV)) {
my ($name, $address) = @{$row}[0,3];
next unless $address =~ $RE{net}{IPv4};
if ($data{$name}) {
$data{$name}[3] .= ','.$address;
}
else {
push @names, $name;
$data{$name} = $row;
}
}
for my $name (@names) {
$csv->print(*STDOUT, $data{$name});
}
<强>更新强>
这是一个忽略在第四个字段中没有有效IPv4地址的记录的版本。我已经使用Regexp::Common
,因为它是获得复杂正则表达式模式的最简单方法。它可能需要在您的系统上安装。
{{1}}
答案 1 :(得分:1)
我建议您使用像Text::CSV
这样的CSV解析器来解决此类问题。
Borodin
已经贴了一个如何做到这一点的好例子。
我建议 NOT 使用的方法之一是正则表达式。
以下单行演示了如何做到这一点,但与实际的csv解析器相比,这是一种非常脆弱的方法:
perl -0777 -ne '
while (m{^((.*)"[^"\n]*"\n(?:(?=\2).*\n)*)}mg) {
$s = $1;
$s =~ s/"\n.*"([^"\n]+)(?=")/,$1/g;
print $s
}' test.csv
输出:
"2B_AppName-Environment","desc","tag","192.168.1.1,192.168.22.155,10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4,192.192.192.40,192.168.50.5"
切换:
-0777
:覆盖整个文件-n
:为输入文件中的每个“行”创建一个while(<>){...}
循环。 -e
:告诉perl
在命令行上执行代码。 <强>代码强>:
while (m{^((.*)"[^"]*"\n(?:(?=\2).*\n)*)}mg)
:将文字分隔为匹配的部分。$s =~ s/"\n.*"([^"\n]+)(?=")/,$1/g;
:在匹配的部分用逗号加入所有IP地址。print $s
:打印结果。