Более элегантный способ доступа к хеш-массивам - PullRequest
3 голосов
/ 21 июня 2011

Во-первых, извинения, если я получаю неправильную терминологию в предстоящем посте, это все еще очень ново для меня.

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

например, проверка, чтобы убедиться, что syslog настроен, добавляется в массив с именем @ internalsyslogerror

после того, как все проверки выполнены, массивы для всех проверок добавляются в хеш сключом является имя устройства.

обратите внимание, что во всем коде используется строгий код, используйте предупреждения

со следующей командой:

$results{$configs} = [@internalsyslogerror, @bordersyslogerror, 
@borderntperror, @borderntperror, @internalntperror, 
@bordertacacserror, @internaltacacserror, @enablepasswordchecks,
@internalsnmpkeyserror, @timezoneerror, @configregistererror, 
@bannererror, @bootregistererror, @domainnameerror];

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

Вот в настоящее время, что я делаю, чтобы отменить ссылку и положить в массив, который язатем отправьте по электронной почте.

foreach my $k (keys %results) {
    push @results, "<b>$k</b><br>";
    if (defined $results{$k}[0] ){
    push @results, "$results{$k}[0]";
    }
    if (defined $results{$k}[1] ){
    push @results, "$results{$k}[1]";
    }
    if (defined $results{$k}[2] ){
    push @results, "$results{$k}[2]";
    }
    if (defined $results{$k}[3] ){  
    push @results, "$results{$k}[3]";
    }
    if (defined $results{$k}[4] ){
    push @results, "$results{$k}[4]";
    }
    if (defined $results{$k}[5] ){
    push @results, "$results{$k}[5]";
    }
    if (defined $results{$k}[6] ){
    push @results, "$results{$k}[6]";
    }
    if (defined $results{$k}[7] ){
    push @results, "$results{$k}[7]";
    }
    if (defined $results{$k}[8] ){
    push @results, "$results{$k}[8]";
    }
    if (defined $results{$k}[9] ){
    push @results, "$results{$k}[9]";
    }
    if (defined $results{$k}[10] ){
    push @results, "$results{$k}[10]";
    }
    if (defined $results{$k}[11] ){
    push @results, "$results{$k}[11]";
    }
    if (defined $results{$k}[12] ){
    push @results, "$results{$k}[12]";
    }
    if (defined $results{$k}[13] ){
    push @results, "$results{$k}[13]";
    }
}

Вопрос в том, могу ли я сделать то, что делаю выше, но каким-то образом сгенерировать код "на лету"

Спасибо

Ответы [ 5 ]

5 голосов
/ 21 июня 2011
foreach my $k (keys %results) { 
   push @results, "<b>$k</b><br>"; 
   for my $result (@{$results{$k}) {
       next if (!defined $result);
       push @results, $result;
   }
}

или даже

foreach my $k (keys %results) { 
   push @results, "<b>$k</b><br>"; 
   push @results, grep { defined $_ } @{$results{$k}};
}

edit: исправлена ​​опечатка при последнем нажатии ...

4 голосов
/ 21 июня 2011

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

foreach my $k (keys %results) {
    push @results, "<b>$k</b><br>";
    foreach my $index ( 0..$#{$results{$k}} ) {
        if (defined $results{$k}[$index] ){
            push @results, "$results{$k}[$index]";
        }
    }
}

Вышесказанное заменяет весь ваш foreach / if конструкт.

3 голосов
/ 21 июня 2011

Используйте цикл for:

for ($i = 0; $i < 14; $i++) {
  ...
}
2 голосов
/ 21 июня 2011

Мне кажется, что этот код .... неисправен. Поправь меня, если я ошибаюсь.

$results{$configs} = [@internalsyslogerror, @bordersyslogerror, ... ];

Это создаст только один длинный массив скалярных значений , а не массив массивов . Учтите это:

C:\perl>perl -we "@a=qw(1 2 3 a); @g=(11,22,33,44); $b{key}=[@a,@g]; print qq(@{$b{key}},\n); print qq($b{key}[0]);"
1 2 3 a 11 22 33 44,
1

Это ясно демонстрирует, что $b{key} содержит все значения как @a, так и @g, и что $b{key}[0] просто ссылается на первое значение в первом массиве, то есть $a[0].

Чтобы сделать то, что вы, кажется, делаете, то есть собрать журналы в отдельные переменные под определенным ключом, вам нужно использовать ссылки на массивы:

$results{$config} = [\@internalsyslogerror, \@bordersyslogerror, ...];

Единственный способ увидеть, как работает ваше текущее решение, - это если у вас в коде ранее была ошибка / функция (предназначенная или непреднамеренная), в результате чего первое значение в каждом массиве содержит все данные, относящиеся к этой категории, например:

$internalsyslogerror[0] = "device A not responding, shutting down.\ndevice A rebooted.\nyada yada\n ....";

Если это так, то то, что вы делаете, эквивалентно:

$results{$config} = [ $internalsyslogerror[0], $bordersyslogerror[0], ...];

Но если вы когда-нибудь получите два значения в одном из массивов, то ваша система будет испорчена, и в конце вашего отчета будет ошибка «по одному». Если вы не используете динамический цикл for для push значений, но он все равно будет грязным.

Кроме того, исключая неопределенные значения, ваша внутренняя структура - которая зависит только от порядка - будет испорчена, так что вы не будете знать, происходит ли первое значение в @results от @internalsyslogerror или @bordersyslogerror.

Вывод:

Если вы довольны тем, что ваша текущая система работает хорошо, просто используйте цикл for, как советуют другие. Используйте динамическое значение вместо абсолютного. Мне нравится решение FMC (слегка измененное):

# Solution by FMc
for my $k (keys %results) {
    push @results, "<b>$k</b><br>";
    push @results, grep { defined $results{$k}[$_] } 0 .. $#results{$k};
}

Если, однако, вы хотите сохранить внутреннюю структуру, вы не можете исключить неопределенные значения и не можете объединить все различные массивы в один, если данные уже не объединены в строки в первом значении массива. Итак:

$results{$config} = [ \@array1, \@array2, ...];

....

for my $key (keys %results) {
    push @results, "<b>$key</b><br>";
    my $i=0;
    for my $ref (@{$results{$key}]) {
        push @results, "Array $i:\n<br>" . (defined @$ref ? "@$ref" : "");
        $i++;
    }
}

Форматирование вывода также может быть точно настроено. Вместо объединения строк, например "@array", вы можете сделать явное соединение: join("<br>\n", @array).

1 голос
/ 21 июня 2011

Если вы ничего не делаете с ключом, это эквивалентно:

@results = map { "$_" } grep {; defined } map { @$_ } values %results;
...