Perl две последовательные команды возврата галочкой не запускаются - PullRequest
1 голос
/ 17 мая 2011

Я пытаюсь написать Perl-скрипт для управления экземплярами amazon ec2. В части моего кода у меня есть две команды оболочки, и когда я их вызываю, запускается первая, но не вторая. Я не могу найти хорошее объяснение этому. Вот код:

$run_instances = 'ec2-run-instances ami-8e1fece7 -k mykey -t t1.micro';
$outp = `$run_instances`;
$outp =~ /INSTANCE\s+(i-\w+)\s/;
$instance_id = $1;
$describe_instances = "ec2-describe-instances $instance_id";
$outp = `$describe_instances`;
$outp =~ /(ec2-(\d+-\d+-\d+-\d+).\S+)/;

Проблема в $outp, здесь вывод $run_instances. Некоторое время я не мог понять, почему я получаю неправильный вывод; тогда я понял, что $describe_instances команда не запускается.

Я посмотрел на значение $describe_instances, вызвал его из оболочки Linux, и он работал нормально. Я вызвал его из другого сценария Perl, и он работал нормально.

Затем я дал $outp вывод, который захватывается при запуске $run_instances ($outp = "INSTANCE ......"). Это сработало, поэтому мне пришла в голову мысль, что почему-то, когда эти две команды запускаются последовательно, вторая не запускается.

Еще одна вещь, которую стоит отметить, это то, что когда я помещаю вышеуказанный код в цикл каждый раз, $run_instances работает, но $describe_instances - нет.

Я был бы очень рад, если бы вы могли пролить свет на это:)

Спасибо

Ответы [ 3 ]

4 голосов
/ 17 мая 2011

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

Красный флаг 1

Код из вашего вопроса пытается выполнить внешние команды с обратными галочками и предполагает успех.Вы должны всегда проверять состояние любого вызова операционной системы.Неудачный `$command` - также известный как qx// или readpipe - становится известным одним или несколькими способами в зависимости от того, была ли выполнена команда ине удалось или произошла ошибка при попытке выполнения команды:

  • Значение специальной переменной $? не равно нулю.
  • В случае неудачного выполнения
    • специальная переменная $! содержит описание ошибки.
    • оператор возвращает неопределенное значение в скалярном контексте или пустой список в контексте списка.

С Perl `$command` выполняет подоболочку для выполнения $command, поэтому подоболочка может быть программой, которая запускается и завершается неудачей, например ,для неправильного синтаксиса команды.

На моей машине успешно выполняется только одна команда в приведенном ниже примере.

#! /usr/bin/env perl

use strict;
use warnings;

my @commands = (
  "bad syntax (",
  "does-not-exist",
  "/etc/passwd",
  "perl --no-such-option",
  "perl -le 'print q(Hello world)'",
);

foreach my $c (@commands) {
  print "Capturing output of command $c...\n";
  my $output = `$c`;

  if ($? == 0) {
    print "  - command executed successfully!\n";
  }
  elsif ($? == -1) {
    print "  - command failed to execute: \$!=$!\n";
  }
  else {
    print "  - command exited with status " . ($? >> 8) . "\n";
  }

  print "  - value of \$output is ",
        (defined $output ? "" : "un"), "defined\n\n";
}

Вывод:

Capturing output of command bad syntax (...
sh: Syntax error: "(" unexpected
  - command exited with status 2
  - value of $output is defined

Capturing output of command does-not-exist...
Can't exec "does-not-exist": No such file or directory at ./runcmds line 16.
  - command failed to execute: $!=No such file or directory
  - value of $output is undefined

Capturing output of command /etc/passwd...
Can't exec "/etc/passwd": Permission denied at ./runcmds line 16.
  - command failed to execute: $!=Permission denied
  - value of $output is undefined

Capturing output of command perl --no-such-option...
Unrecognized switch: --no-such-option  (-h will show valid options).
  - command exited with status 29
  - value of $output is defined

Capturing output of command perl -le 'print q(Hello world)'...
  - command executed successfully!
  - value of $output is defined

Красный флаг 2

Обратите внимание, например, на строку вывода

Can't exec "does-not-exist": No such file or directory at ./runcmds line 16.

Я не написал код, который сгенерировал этопредупреждение.Вместо этого это произошло автоматически, потому что я включил прагму предупреждений со строкой use warnings.Если бы вы включили предупреждения, Perl уже дал бы вам хотя бы подсказку о причине вашей проблемы, поэтому я предполагаю, что вы не включали предупреждения.

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

Red Flag 3

Наконец, вы безоговорочно используете regex capture-variable $1, и эта привычка приведет к неожиданным ошибкам, когдасовпадение не удается, и вы получаете захваченное значение из другого совпадения.Вы всегда должны заключать в себе условные обозначения $1, $2 и друзей в условном выражении, например ,

if (/pa(tte)rn/) {
  do_something_with $1;
}

Рекомендуемые исправления

В верхней частикод сразу после строки shebang, вы должны немедленно добавить строку

use warnings;

Я бы также порекомендовал

use strict;

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

Используйте output_of ниже, чтобы зафиксировать вывод команды или умереть с соответствующей диагностикой.

sub output_of {
  my($cmd) = @_;
  my $output = `$cmd`;
  return $output if $? == 0;

  if ($? == -1) {
    die "$0: $cmd failed to execute: $!\n";
  }
  elsif ($? & 127) {
    my $signal = $? & 127;
    my $core = ($? & 128) ? ", core dumped" : "";
    die "$0: $cmd died with signal $signal$core\n";
  }
  else {
    die "$0: $cmd exited with status " . ($? >> 8) . "\n";
  }
}

Раздел кода вашего вопроса становится

my $output;
$output = output_of 'ec2-run-instances ami-8e1fece7 -k mykey -t t1.micro';
if ($output =~ /INSTANCE\s+(i-\w+)\s/) {
  my $instance_id = $1;

  $output = output_of "ec2-describe-instances $instance_id";
  if ($output =~ /(ec2-(\d+-\d+-\d+-\d+).\S+)/) {
    print "$0: found instance $2\n";  # or whatever
  }
  else {
    die "$0: no ec2-IP in ec2-describe-instances output:\n$output";
  }
}
else {
  die "$0: no INSTANCE in ec2-run-instances output:\n$output";
}

С этими изменениями ваш код будет использовать стандартную диагностику ошибок для всех режимов отказа при обработке вывода из ec2-run-instances и ec2-describe-instances вместо того, чтобы оставлять васинтересно, что пошло не так.

1 голос
/ 17 мая 2011

Является ли $ outp определенным или неопределенным после:

$outp = `$describe_instances`;

Вы убедились, что $ instance_id установлен на то, что вы ожидаете? Каковы значения $! и $? после нерабочей команды backtick? Что-то записывается в stderr нерабочей командой?

0 голосов
/ 17 мая 2011

Я подозреваю, что проблема в этой строке:

$instance_id = $1;

$1 - глобальная переменная.Никогда не следует использовать $1 и другие непроверенные, поскольку оно:

Содержит подшаблон из соответствующего набора скобок для захвата из последнего успешного совпадения с образцом

(из perldoc perlvar )

Другими словами, он не перезаписывается, если не найдено совпадений.Что вы можете сделать, это что-то вроде этого:

die $! unless ( $outp =~ /INSTANCE\s+(i-\w+)\s/ );
$instance_id = $1;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...