linux - 使用 Ctrl-C (SIGINT) 在 Perl 中使用系统命令打破 while 循环?

标签 linux perl

考虑以下示例,test.pl:

#!/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 循环,一切都如预期的那样:

$ 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-C,程序立即退出。

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

$ 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
$ 

所以在上面的代码片段中,我疯狂地按下 Ctrl-C(^C),程序完全忽略了我 :/ 然后我通过按 Ctrl-Z(^Z),这会停止进程并在后台设置;然后在生成的 shell 中执行 killall perl,然后执行 fg 命令,这会将 Perl 作业放回前台 - 它最终因杀死所有

我想要的是运行这样的系统循环,并可以使用通常的 Ctrl-C 中断/退出它。这可能吗?我该怎么做?

最佳答案

Perl 的信号处理机制将信号处理推迟到安全点。在 perl VM 的操作码之间检查延迟信号。由于 system 和 friends 算作一个操作码,只有在执行命令终止后才会检查信号。

这可以通过 forking 来规避,然后在循环中等待子进程终止。 child 也可以通过信号处理程序提前终止。

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

可能有很多方法会出错(在 child 生成之前获得 SIGINT……)。我还省略了任何错误处理。

关于linux - 使用 Ctrl-C (SIGINT) 在 Perl 中使用系统命令打破 while 循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18427459/

相关文章:

sql-server - Red Hat Linux 上的 SSIS - 支持吗?

linux - 循环的 bash 脚本

linux - SIGSEGV 的原因

perl - 如何使用 Perl 修剪字符串的最后部分?

regex - 我想解释 Perl 的正则表达式引擎的行为

windows - Perl 模块手册安装 Windows XP

Perl Getopt::Long - 仅对定义的参数使用子参数

linux - 配置: error: "freeradius headers not found"

linux - 就地编辑文件

c++ - 带有 native 库的 Perl 模块 : Possible to get XSLoader to use RTLD_GLOBAL on dlopen?