修改:修改代码和输出以使其更清晰
编辑2:添加了用于复制的示例输入
我有一个JSON文件和一个CSV文件,我正在对这两个文件进行比较。问题是$ asset_ip在外部foreach循环中正确定义,但是在嵌套循环中$ asset_ip变为未定义。
为什么$ asset_ip变得未定义?
#!/usr/bin/perl
# perl -e'use CPAN; install "Text::CSV"'
use strict;
use warnings;
use JSON::XS;
use File::Slurp;
use Text::CSV;
my $csv = Text::CSV->new( { sep_char => ',' } );
my $csv_source = "servers.csv";
my $json_source = "assets.json";
my $dest = "servers_for_upload.csv";
# defined these here as I need to use them in foreach loop and if statement:
my $csv_ip;
my @fields;
open( my $csv_fh, '<', $csv_source ) or die "$! error trying to read";
open( my $dest_fh, '>', $dest ) or die "$! error trying to read";
my $json = read_file($json_source);
my $json_array = decode_json $json;
foreach my $item (@$json_array) {
my $id = $item->{id};
my $asset_ip = $item->{interfaces}->[0]->{ip_addresses}->[0]->{value};
# test the data is there:
if ( defined $asset_ip ) {
print "id: " . $id . "\nip: " . $asset_ip . "\n";
}
while (my $line = <$csv_fh>) {
chomp $line;
if ( $csv->parse($line) ) {
@fields = $csv->fields();
$csv_ip = $fields[0];
}
else {
warn "Line could not be parsed: $line\n";
}
if ( $csv_ip eq $asset_ip ) {
# preppend id to csv array and write these lines to new file
unshift( @fields, $id );
print $dest_fh join( ", ", @fields );
}
}
}
close $csv_fh;
输出:
Use of uninitialized value $asset_ip in string eq at script.pl line 43, <$csv_fh> line 1.
Use of uninitialized value $asset_ip in string eq at script.pl line 43, <$csv_fh> line 2.
Use of uninitialized value $asset_ip in string eq at script.pl line 43, <$csv_fh> line 3.
id: 1003
ip: 192.168.0.2
id: 1004
ip: 192.168.0.3
id: 1005
ip: 192.168.0.4
assets.json:
[{"id":1001,"interfaces":[]},{"id":1003,"interfaces":[{"ip_addresses":[{"value":"192.168.0.2"}]}]},{"id":1004,"interfaces":[{"ip_addresses":[{"value":"192.168.0.3"}]}]},{"id":1005,"interfaces":[{"ip_addresses":[{"value":"192.168.0.4"}]}]}]
请注意,对于第一次迭代,$ asset_ip 将未定义。因此,如果定义了$ asset_ip,我将更改代码以仅运行eq比较。但是,对于此示例,我没有进行检查,因为所有迭代都是未定义的。
servers.csv:
192.168.0.3,Brian,Germany
192.168.0.4,Billy,UK
192.168.0.5,Ben,UK
答案 0 :(得分:3)
我认为你的问题是:
foreach my $line (<$csv_fh>) {
您在我们的外循环中执行此操作。但是当你这样做时,你的$csv_fh
会在文件末尾结束。
完成此操作后,外部循环的后续迭代将不会执行此内部循环,因为它无需从$csv_fh
读取任何内容。
如果这是您的问题,一个简单的测试是添加seek
,例如seek ( $csv_fh, 0, 0 );
。
但这不是一件有效的事情,因为那时你将多次循环遍历文件 - 你应该把它读入数据结构并使用它。
编辑:这是你的问题:
[{"id":1001,"interfaces":[]},{"id":1003,"interfaces":[{"ip_addresses":[{"value":"192.168.0.2"}]}]},{"id":1004,"interfaces":[{"ip_addresses":[{"value":"192.168.0.3"}]}]},{"id":1005,"interfaces":[{"ip_addresses":[{"value":"192.168.0.4"}]}]}]
具体而言:
[{"id":1001,"interfaces":[]}
该数组中的第一个元素没有定义$asset_ip
。
这意味着 - 在第一次传递时,$asset_ip
未定义并生成错误。 (由于您的if defined
测试,没有打印任何行)。
然后 - 代码继续遍历$csv_fh
- 读到文件末尾 - 寻找匹配(并失败3次,生成3条错误消息。
第二次迭代 - 对于id 1002
- IP无论如何都不在文件中,但$csv_fh
已被读取到文件结束(EOF) - 因此{{1} }循环根本不执行。
可以通过以下方式使可行
foreach
之后添加else next;
。if defined
。 但实际上 - 重写是有序的,所以你不会反复重读文件。
非常粗暴:
seek
我建议这也需要:
#!/usr/bin/perl
# perl -e'use CPAN; install "Text::CSV"'
use strict;
use warnings;
use JSON::XS;
use File::Slurp;
use Text::CSV;
my $csv = Text::CSV->new( { sep_char => ',' } );
my $csv_source = "servers.csv";
my $json_source = "assets.json";
my $dest = "servers_for_upload.csv";
# defined these here as I need to use them in foreach loop and if statement:
my $csv_ip;
my @fields;
open( my $csv_fh, '<', $csv_source ) or die "$! error trying to read";
open( my $dest_fh, '>', $dest ) or die "$! error trying to read";
my $json = read_file($json_source);
my $json_array = decode_json $json;
foreach my $item (@$json_array) {
my $id = $item->{id};
my $asset_ip = $item->{interfaces}->[0]->{ip_addresses}->[0]->{value};
# test the data is there:
if ( defined $asset_ip ) {
print "id: " . $id . "\nip: " . $asset_ip . "\n";
}
else {
print "asset_ip undefined for id $id\n";
next;
}
while ( my $line = <$csv_fh> ) {
chomp $line;
if ( $csv->parse($line) ) {
@fields = $csv->fields();
$csv_ip = $fields[0];
}
else {
warn "Line could not be parsed: $line\n";
}
if ( $csv_ip eq $asset_ip ) {
# preppend id to csv array and write these lines to new file
unshift( @fields, $id );
print {$dest_fh} join( ", ", @fields ),"\n";
}
}
seek( $csv_fh, 0, 0 );
}
close $csv_fh;
,因此使用Text::CSV
似乎不是一致的选择。如果您的数据保证print join ( ","...
,则值得保留输出。