1

Amazon ec2 インスタンスを管理するための perl スクリプトを作成しようとしています。コードの一部に 2 つのシェル コマンドがあり、それらを呼び出すと、最初のコマンドは実行されますが、2 番目のコマンドは実行されません。これについての良い説明が見つからないようです。コードは次のとおりです。

$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_instancesLinux シェルから呼び出された の値を調べたところ、問題なく動作しました。別の Perl スクリプトから呼び出したところ、問題なく動作しました。

次に、実行$outp時にキャプチャされる出力を示しました$run_instances( $outp = "INSTANCE ......")。うまくいったので、どういうわけか、これら2つのコマンドを連続して実行すると、2番目のコマンドが実行されないという考えが浮かびました。

もう1つ注意すべきことは、上記のコードをループに入れると、毎回$run_instances機能しますが$describe_instances機能しないということです。

あなたがこれについていくつかの光を提供できれば、私は本当に幸せです:)

ありがとうございました

4

3 に答える 3

4

あなたのプログラムにはいくつかの危険信号があります。以下でそれらについて説明し、最後にコードを記述するより良い方法を提案します。

赤旗 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-instancesec2-describe-instances

于 2011-05-17T12:46:12.707 に答える
1

次の後に $outp が定義されているか定義されていないか:

$outp = `$describe_instances`;

$instance_id が期待どおりに設定されていることを確認しましたか? $! の値は何ですか? と $? 動作しないバックティック コマンドの後? 動作しないコマンドによって stderr に何かが書き込まれていますか?

于 2011-05-17T06:24:07.353 に答える
0

問題は次の行にあると思われます。

$instance_id = $1;

$1グローバル変数です。$1次の理由から、et al untested を使用しないでください。

最後に成功した パターン マッチからの対応する一連のキャプチャ括弧からのサブパターンが含まれます

( perldoc perlvarから)

つまり、一致しなければ上書きされません。あなたができることは次のようなものです:

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

私は決してテストされていないものを使用し$1ないようにしています。もちろん、それが適切な場所から来ていることを確認する方法はたくさんあります。

于 2011-05-17T12:11:44.833 に答える