2015-09-23 2 views
-1

Edit: модифицированный код и выход, чтобы сделать его более яснымпеременная Perl становится неопределенным в цикле

Редактировать 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 будет быть неопределенным. Поэтому я буду изменять код, чтобы запускать сравнение eq, если определен параметр $ asset_ip. Однако для этого примера я не выполняю проверку, потому что все итерации не определены.

Servers.csv:

192.168.0.3,Brian,Germany 
192.168.0.4,Billy,UK 
192.168.0.5,Ben,UK 
+5

Не связано с вашей проблемой: у вас должны быть двойные кавычки, а не одинарные кавычки, в 'die '$! ошибка, пытаясь прочитать ». Как и в случае с этими ошибками, вы получите '$!' Вместо значения переменной. –

+2

Какое доказательство у вас есть, что $ asset_ip не определено во внутреннем цикле? Похоже, что ваш внутренний цикл не выполняется. Является ли proton.csv пустым? – toolic

+0

Извините - не упоминал - я получаю ошибки, что $ asset_ip не определен при запуске скрипта. – Jamie

ответ

3

Я думаю, ваша проблема будет это:

foreach my $line (<$csv_fh>) { 

Вы выполнить это в нашем внешнем контуре. Но когда вы это сделаете, ваш $csv_fh заканчивается в конце файла.

После того, как вы сделали это, последующие итерации вашего внешнего цикла не будет выполнять эту внутреннюю петлю, потому что не осталось ничего для того, чтобы читать из $csv_fh.

Простой тест, если это ваша проблема состоит в том, чтобы добавить seek, например. seek ($csv_fh, 0, 0);.

Но это не эффективная вещь, потому что тогда вы будете перебирать файл несколько раз - вместо этого вы должны прочитать его в структуре данных и использовать это.

Edit: Вот ваша проблема:

[{"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 сообщений об ошибках.

Вторая итерация - для идентификатора 1002 - IP не в файле в любом случае, но $csv_fh уже считаны конца-файла (EOF) - так что foreach цикл не выполняется вообще.

Это можно сделать работоспособным по:

  • добавления else next; после этого 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 (","... не кажется последовательным выбором. Если ваши данные требуют Text::CSV, то стоит сохранить его и для вывода.
+1

Я только что написал остроумный комментарий в сводке _edit _... :( – simbabque

+0

Я просто представлю себе что-то веселое и проницательное. – Sobrique

+0

Я шел по линиям «с открытым исходным кодом не имел в виду, что у нас есть петля OP». – simbabque

Смежные вопросы