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

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

我已经build立了一个最低工作的例子。 这是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"); 

这里是外部的“程序”:

 #!/bin/env bash echo "Started $1" sleep 3 echo "Finished $1" 

我启动了脚本并发出了一个CTRL + Z.然后我做了一个ps -o pid,pgrp,cmd来列出进程信息。 我可以看到脚本,它调用的bash shell和sleep命令都属于同一个进程组。 当sleep正在执行时,我也希望成为前台进程组。 我已经读了一些Linux,信号和terminal,发现CTRL + C应该发送一个中断到整个前台进程组,因此也终止了脚本。

我也尝试使用exec()而不是system() 。 这样脚本在触发CTRL + C时就停止了,但是它不会跑过test1

任何人都可以点亮这里发生的事情吗?

你已经调查和了解过程组是很好的。 解释这些通常是这样一个问题的难点。 所以你知道中断信号SIGINT应该被发送到进程组中的所有进程,包括你的perl进程。

但是, SIGINT并不总是致命的。 它可以被捕获或被忽略。 当子进程正在运行时, system()函数会忽略SIGINT 。 这个功能可以让你中断一个坏的子进程,而不会杀死父进程。

在perlfunc中, system()的描述表示:

由于“SIGINT”和“SIGQUIT”在执行“system”期间被忽略,如果你希望程序在接收到这些信号时终止,你将需要根据返回值自行安排。

 @args = ("command", "arg1", "arg2"); system(@args) == 0 or die "system @args failed: $?" 

这是你需要做的。 检查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_the_test做到这run_the_test

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