1

我正在尝试编写用于管理亚马逊 ec2 实例的 perl 脚本。在我的部分代码中,我有两个 shell 命令,当我调用它们时,第一个运行但第二个不运行。我似乎找不到一个很好的解释。这是代码:

$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 shell 调用它,它运行良好。我从另一个 Perl 脚本中调用它,它运行良好。

然后我给出$outp了运行时捕获的输出$run_instances$outp = "INSTANCE ......")。它起作用了,所以我想到当这两个命令连续运行时,第二个命令不会运行。

还要注意的一件事是,当我将上述代码放入循环中时,每次都$run_instances有效但$describe_instances无效。

如果您能对此有所了解,我会非常高兴:)

谢谢

4

3 回答 3

4

您的程序有一些危险信号。我在下面讨论它们,最后提出一种更好的编写代码的方法。

红旗 1

您问题中的代码尝试使用反引号执行外部命令并假设成功。您应该始终检查对操作系统的任何调用的状态。失败的`$command`——也称为qx//or ——readpipe根据命令是否执行并失败或尝试执行命令是否失败,以一种或多种方式使自己知道:

  • 特殊变量的值$?非零。
  • 在执行失败的情况下
    • 特殊变量$!包含失败的描述。
    • 运算符返回标量上下文中的未定义值或列表上下文中的空列表。

使用 Perl,`$command`为了执行 执行子shell $command,因此子shell 可能是执行并失败的程序,例如,错误的命令语法。

下面示例中的一个命令在我的机器上成功。

#! /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 的值

捕获命令的输出不存在...
无法执行“不存在”:./runcmds 第 16 行没有这样的文件或目录。
  - 命令执行失败:$!=没有这样的文件或目录
  - $output 的值未定义

正在捕获命令 /etc/passwd 的输出...
无法执行“/etc/passwd”:./runcmds 第 16 行的权限被拒绝。
  - 命令执行失败:$!=Permission denied
  - $output 的值未定义

正在捕获命令 perl --no-such-option... 的输出
无法识别的开关:--no-such-option(-h 将显示有效选项)。
  - 命令以状态 29 退出
  - 定义了 $output 的值

捕获命令 perl -le 'print q(Hello world)' 的输出...
  - 命令执行成功!
  - 定义了 $output 的值

红旗 2

注意例如输出行

无法执行“不存在”:./runcmds 第 16 行没有这样的文件或目录。

我没有编写生成此警告的代码。相反,它是自动发生的,因为我使用该行启用了警告编译指示use warnings。如果你启用了警告,Perl 至少会提示你问题的原因,所以我假设你没有启用警告。

这是另一个危险信号。警告可以帮助您。始终为任何重要的程序启用它们。

红旗 3

最后,您$1无条件地使用正则表达式捕获变量,当匹配失败并且您从不同的匹配中获取捕获的值时,这种习惯会导致令人惊讶的错误。您应该始终将 , 和 Friends 的用法包含$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-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,因为它:

包含来自上次成功 模式匹配的相应捕获括号集的子模式

(来自perldoc perlvar

换句话说,如果没有匹配,它不会被覆盖。你可能会做的是这样的:

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

我尽量不要使用$1未经测试的,当然有很多方法可以确保它来自正确的地方。

于 2011-05-17T12:11:44.833 回答