首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用Ctrl (SIGINT)在Perl中使用系统命令来突破with循环?

使用Ctrl (SIGINT)在Perl中使用系统命令来突破with循环?
EN

Stack Overflow用户
提问于 2013-08-25 09:19:20
回答 2查看 3K关注 0票数 4

考虑下面的示例,test.pl

代码语言:javascript
复制
#!/usr/bin/env perl

use 5.10.1;
use warnings;
use strict;

$SIG{'INT'} = sub {print "Caught Ctrl-C - Exit!\n"; exit 1;};

$| = 1; # turn off output line buffering

use Getopt::Long;
my $doSystemLoop = 0;

GetOptions( "dosysloop"=>\$doSystemLoop );
print("$0: doSystemLoop is:$doSystemLoop (use " . (($doSystemLoop)?"system":"Perl") . " loop); starting...\n");

my $i=0;

if (not($doSystemLoop)) { # do Perl loop

    while ($i < 1e6) {
      print("\tTest value is $i");
      $i++;
      sleep 1;
      print(" ... ");
      sleep 1;
      print(" ... \n");
    }

} else {                  # do system call loop

    while ($i < 1e6) {
      system("echo","-ne","\tTest value is $i");
      $i++;
      system("sleep 1");
      system("echo","-ne"," ... ");
      system("sleep 1");
      system("echo","-e"," ... ");
    }

}

所以,如果我调用这个程序,所以它使用了一个通常的Perl循环,那么一切都如预期的那样:

代码语言:javascript
复制
$ perl test.pl
test.pl: doSystemLoop is:0 (use Perl loop); starting...
    Test value is 0 ...  ... 
    Test value is 1 ...  ... 
    Test value is 2 ... ^CCaught Ctrl-C - Exit!
$

..。也就是说,我击中了Ctrl,程序立即退出。

但是,如果while循环的命令主要由system调用组成,那么使用Ctrl退出几乎是不可能的:

代码语言:javascript
复制
$ perl test.pl --dosysloop
test.pl: doSystemLoop is:1 (use system loop); starting...
    Test value is 0 ...  ... 
    Test value is 1 ...  ... 
    Test value is 2 ... ^C ... 
    Test value is 3 ... ^C ... 
    Test value is 4 ... ^C ... 
    Test value is 5^C ... ^C ... 
    Test value is 6^C ... ^C ... 
    Test value is 7^C ... ^C ... 
    Test value is 8^C ... ^C ... 
    Test value is 9^C ... ^C ... 
    Test value is 10 ... ^C ... 
    Test value is 11^C ... ^C ... 
    Test value is 12^C ...  ... 
    Test value is 13^Z
[1]+  Stopped                 perl test.pl --dosysloop
$ killall perl
$ fg
perl test.pl --dosysloop
Terminated
$ 

因此,在上面的片段中,我像mad一样点击Ctrl( ^C),程序完全忽略了我,然后我通过点击Ctrl( ^Z)作弊,它停止进程并设置在后台;然后在生成的shell中执行killall perl,然后执行fg命令,它将Perl作业放回前台--在这个命令中,它最终由于killall而终止。

我想要的是,像这样运行一个系统循环,可以用通常的Ctrl来打破它/退出它。这是可能的吗,我该怎么做?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-08-25 09:44:19

Perl的信号处理机制将信号处理推迟到安全点。延迟信号在perl的Opcode之间进行检查。当system和朋友计算为单一操作码时,只有在执行命令终止后才会检查信号。

这可以通过forking绕过,然后在循环中等待子进程终止。还可以通过信号处理程序提前终止子节点。

代码语言:javascript
复制
sub launch_and_wait {
  my $wait = 1;
  my $child;

  local $SIG{CHLD} = sub {
    $wait = 0;
  };
  local $SIG{INT}  = sub {
    $wait = 0;
    kill KILL => $child if defined $child;
  };

  if ($child = fork()) {
    # parent

    while ($wait) {
      print "zzz\n";
      sleep 1;
    }
    wait; # try to join the child

  } else {
    # child

    exec {$_[0]} @_;

  }
}

launch_and_wait sleep => 60;
print "Done\n";

这可能有很多种可能出错的方法(在孩子生成…之前获得一个SIGINT )。我还省略了任何错误处理。

票数 2
EN

Stack Overflow用户

发布于 2013-08-26 07:41:14

检查system()命令的退出状态以确定任何信号。被SIGINT中断的外部命令在这里将得到"2“:

代码语言:javascript
复制
while () {
    system("sleep", 1);
    if ($? & 127) {
        my $sig = $? & 127;
        die "Caught signal INT" if $sig == 2; # you may also abort on other signals if you like
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18427459

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档