perl 两个连续的反勾 shell 命令不运行

发布于 2024-11-08 05:03:46 字数 940 浏览 4 评论 0原文

我正在尝试编写 perl 脚本来管理亚马逊 ec2 实例。在我的部分代码中,我有两个 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 命令没有运行。

我查看了从 Linux shell 调用的 $describe_instances 的值,它工作得很好。我从另一个 Perl 脚本调用它,它工作得很好。

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

还要注意的另一件事是,当每次 $run_instances 工作但 $describe_instances 不工作时,我将上面的代码放入循环中。

如果您能对此提供一些说明,我将非常高兴:)

谢谢

I am trying to write perl script for managing amazon ec2 instances. In part of my code, I have two shell commands, and when I invoke them, the first runs but not the second. I cannot seem to find a good explanation for this. Here is the code:

$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+)/;

The problem is $outp here has the output of $run_instances. For some time I could not understand why I was getting the wrong output; then I realised that $describe_instances command does not run.

I looked at the value of $describe_instances, called that from the Linux shell, and it worked fine. I called it from another Perl script, and it worked fine.

Then I gave $outp the output that is captured when $run_instances runs ($outp = "INSTANCE ......"). It worked, so I got the idea that somehow when these two commands run consecutively the second does not run.

One more thing to note is that when I put above code in a loop every time $run_instances works but $describe_instances does not.

I would be really happy if you could provide some light on this :)

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

泛滥成性 2024-11-15 05:03:46

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

危险信号 1

您问题中的代码尝试使用反引号执行外部命令并假设成功。您应该始终检查对操作系统的任何调用的状态。失败的 `$command` — 还有称为 qx//readpipe — 以一种或多种方式显示自身,具体取决于是否命令已执行但失败或尝试执行命令是否失败:

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

使用 Perl,`$command` 执行一个子 shell 以便执行 $command,因此子 shell 可能是执行并失败的程序,eg,命令语法错误。

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

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

Red Flag 2

请注意,例如输出行,

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

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

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

危险信号 3

最后,您无条件地使用正则表达式捕获变量 $1,当匹配失败并且您从不同的匹配中获取捕获的值时,这种习惯将导致令人惊讶的错误。您应该始终将 $1$2 和朋友的使用包装在条件(eg)内,

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

建议的修复

位于代码顶部附近,紧接在 行,

use warnings;

shebang 行,您应该立即添加我也推荐的

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 的输出中的所有故障模式提供标准错误诊断 而不是让你想知道出了什么问题。

Your program has a few red flags. I discuss them below and at the end suggest a better way to write your code.

Red Flag 1

The code from your question attempts to execute external commands with backticks and assumes success. You should always check the status of any call to the operating system. A failed `$command`—also known as qx// or readpipe—makes itself known in one or more ways depending on whether the command executed and failed or whether attempted execution of the command failed:

  • The value of the special variable $? is non-zero.
  • In cases of failed execution
    • the special variable $! contains a description of the failure.
    • the operator returns the undefined value in scalar context or the empty list in list context.

With Perl, `$command` executes a subshell in order to execute $command, so the subshell may be the program that executes and fails, e.g., for bad command syntax.

Exactly one command in the example below succeeds on my machine.

#! /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";
}

Output:

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

Red Flag 2

Note for example the line of output

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

I didn't write the code that generated this warning. Instead, it happened automatically because I enabled the warnings pragma with the line use warnings. Had you enabled warnings, Perl would have already given you at least a hint about the cause of your problem, so I assume you didn't enable warnings.

This is another red flag. Warnings are there to help you. Always enable them for any non-trivial program.

Red Flag 3

Finally, you're using regex capture-variable $1 unconditionally, and this habit will lead to surprising bugs when the match fails and you get a captured value from different match. You should always wrap uses of $1, $2, and friends inside a conditional, e.g.,

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

Recommended Fixes

Near the top of your code just after the shebang line, you should immediately add the line

use warnings;

I'd also recommend

use strict;

but that will likely require more work to clean up your code. We're here to help you understand and overcome any issues you find in that worthwhile effort.

Use output_of below to capture a command's output or die with an appropriate diagnostic.

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";
  }
}

The section of code from your question becomes

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";
}

With these changes, your code will supply on the standard error diagnostics for all failure modes in processing output from ec2-run-instances and ec2-describe-instances instead of leaving you to wonder what went wrong.

七七 2024-11-15 05:03:46

在以下内容之后 $outp 是已定义还是未定义:

$outp = `$describe_instances`;

您是否已验证 $instance_id 设置为您期望的值?
$的值是多少!和$?在非工作反引号命令之后?
非工作命令是否正在向 stderr 写入任何内容?

Is $outp defined or undefined after the:

$outp = `$describe_instances`;

Have you verified that $instance_id is set to what you expect?
What are the values of $! and $? after the non-working backtick command?
Is anything being written to stderr by the non-working command?

栀子花开つ 2024-11-15 05:03:46

我怀疑问题出在这一行:

$instance_id = $1;

$1 是一个全局变量。人们永远不应该使用未经测试的 $1 等,因为它:

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

(来自perldoc perlvar

换句话说,如果没有匹配,则不会被覆盖。您可能会做这样的事情:

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

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

I suspect the problem is this line:

$instance_id = $1;

$1 is a global variable. One should never use $1 et al untested, since it:

Contains the subpattern from the
corresponding set of capturing
parentheses from the last successful
pattern match

(from perldoc perlvar)

In other words, it is not overwritten if there is no match. What you might do is something like this:

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

I try never to use $1 untested, and of course there are many ways to make sure it comes from the right place.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文