Ваша программа имеет несколько красных флажков.Я обсуждаю их ниже и в конце предлагаю лучший способ написания вашего кода.
Красный флаг 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
вместо того, чтобы оставлять васинтересно, что пошло не так.