Perl变量在循环

时间:2015-09-23 14:28:54

标签: json perl csv

修改:修改代码和输出以使其更清晰

编辑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

1 个答案:

答案 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;
  • 在while循环之后添加if defined

但实际上 - 重写是有序的,所以你不会反复重读文件。

非常粗暴:

seek

我建议这也需要:

  • 更改while,这样您每次都不会重新读取文件
  • 您正在使用#!/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 ( ","...,则值得保留输出。