あなたのプログラムにはいくつかの危険信号があります。以下でそれらについて説明し、最後にコードを記述するより良い方法を提案します。
赤旗 1
質問のコードは、バックティックを使用して外部コマンドを実行しようとし、成功を想定しています。オペレーティング システムへの呼び出しのステータスを常にチェックする必要があります。失敗した— or`$command`
とも呼ばれる —は、コマンドが実行されて失敗したかどうか、またはコマンドの実行の試みが失敗したかどうかに応じて、1 つ以上の方法でそれ自体を認識します。qx//
readpipe
- 特殊変数の値が
$?
ゼロ以外です。
- 実行に失敗した場合
- 特殊変数
$!
には、失敗の説明が含まれています。
- この演算子は、スカラー コンテキストでは未定義の値を返し、リスト コンテキストでは空のリストを返します。
Perl では、`$command`
を実行するためにサブシェルを実行します。$command
そのため、サブシェルは実行して失敗するプログラムである可能性があります。たとえば、コマンド構文が正しくない場合などです。
以下の例の 1 つのコマンドだけが、私のマシンで成功します。
#! /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";
}
出力:
コマンドの不正な構文の出力をキャプチャしています (...
sh: 構文エラー: "(" 予期しない
- コマンドはステータス 2 で終了しました
- $output の値が定義されています
コマンドの出力をキャプチャしていますが存在しません...
Can't exec "does-not-exist": No such file or directory at ./runcmds line 16.
- コマンドの実行に失敗しました: $!=そのようなファイルまたはディレクトリはありません
- $output の値は未定義です
コマンド /etc/passwd の出力をキャプチャしています...
「/etc/passwd」を実行できません: ./runcmds 行 16 で権限が拒否されました。
- コマンドの実行に失敗しました: $!=許可が拒否されました
- $output の値は未定義です
コマンド perl --no- such-option の出力をキャプチャしています...
認識されないスイッチ: --no-such-option (-h は有効なオプションを表示します)。
- コマンドはステータス 29 で終了しました
- $output の値が定義されています
コマンド perl -le 'print q(Hello world)' の出力をキャプチャしています...
- コマンドが正常に実行されました!
- $output の値が定義されています
赤旗 2
たとえば、出力の行に注意してください
Can't exec "does-not-exist": No such file or directory at ./runcmds line 16.
この警告を生成するコードは私が書いたものではありません。代わりに、行で警告プラグマを有効にしたため、自動的に発生しましたuse warnings
。警告を有効にしていれば、Perl は問題の原因について少なくともヒントを提供していたはずなので、警告を有効にしなかったと思います。
これは別の危険信号です。警告はあなたを助けるためにあります。重要なプログラムでは常に有効にしてください。
赤旗 3
最後に、正規表現のキャプチャ変数を無条件で使用しています。$1
この習慣は、一致が失敗し、別の一致からキャプチャされた値を取得したときに、驚くべきバグにつながります。$1
、 、および友人の使用を常に$2
条件内でラップする必要があります。
if (/pa(tte)rn/) {
do_something_with $1;
}
推奨される修正
シバン行の直後のコードの上部近くに、すぐに行を追加する必要があります
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