CTRL + C和Perl的system()函数 - 脚本不会被中断

Tud*_*imi 2 linux perl

我有一个Perl脚本,system()按顺序使用该函数启动某些程序.当脚本正在运行并且程序正在运行时,我输入CTRL + C.我希望程序终止(发生),但我也希望脚本终止(这不会发生).

我已经建立了一个最小的工作示例.这是Perl脚本:

#!/bin/env perl


sub run_the_test {
  local($name, $_) = @_;

  print "Starting $name\n";
  return system("program.bash $name");
}


&run_the_test("test1");
&run_the_test("test2");
&run_the_test("test3");
Run Code Online (Sandbox Code Playgroud)

这是外部的"程序":

#!/bin/env bash

echo "Started $1"
sleep 3
echo "Finished $1"
Run Code Online (Sandbox Code Playgroud)

我启动了脚本并发出了CTRL + Z.然后我做了一个ps -o pid,pgrp,cmd列出进程信息.我可以看到脚本,它调用的bash shell和sleep命令都属于同一个进程组.在sleep执行时我也希望它是前台进程组.我在Linux,信号和终端上读了一下,发现CTRL + C应该向整个前台进程组发送一个中断,从而也终止了脚本.

我也试过用exec()而不是system().这样,脚本在触发CTRL + C时会停止,但它不会运行在test1之后.

任何人都可以了解这里发生的事情吗?

小智 6

你调查和理解过程组是很好的.解释这些通常是这样一个问题的难点部分.因此,您知道中断信号SIGINT应该发送到进程组中的所有进程,包括您的perl进程.

SIGINT并不总是致命的.它可以被捕获或忽略.子进程在运行时system()忽略该函数SIGINT.此功能使您可以中断已损坏的子进程,而不会中断父进程.

在perlfunc中,描述system()说:

由于在执行"系统"期间忽略"SIGINT"和"SIGQUIT",如果您希望程序在收到这些信号时终止,则需要根据返回值自行安排.

@args = ("command", "arg1", "arg2");
system(@args) == 0
    or die "system @args failed: $?"
Run Code Online (Sandbox Code Playgroud)

这是你需要做的.检查返回值system(),并在子进程失败时采取您认为合适的任何操作.如果你想区别于SIGINT其他类型的失败,perlfunc中的下一段将有助于:

如果您想手动检查"系统"的故障,可以通过检查$来检查所有可能的故障模式?像这样:

if ($? == -1) {
    print "failed to execute: $!\n";
}
elsif ($? & 127) {
    printf "child died with signal %d, %s coredump\n",
        ($? & 127),  ($? & 128) ? 'with' : 'without';
}
else {
    printf "child exited with value %d\n", $? >> 8;
}
Run Code Online (Sandbox Code Playgroud)

例如,你可以这样做run_the_test:

my $r = system("program.bash $name");
die "Test interrupted, giving up" if ($? & 127) == 2;
return $r;
Run Code Online (Sandbox Code Playgroud)

  • @TudorTimi是`system()`的大致概述.理解这些过程之间的关系就足够了.它省略了信号处理等细节.salva评论的重点是如果你自己实现一个`system()`类函数,你可以改变细节. (5认同)
  • 如果你不想硬编码`2`,你可以`使用POSIX qw(SIGINT);`来导入`SIGINT`常量. (4认同)
  • 另一个选择是使用`fork`和`exec`来启动子进程而不是`system`. (2认同)