目标 KeyboardInterrupt 到子进程

Target KeyboardInterrupt to subprocess

我希望在 Python 中启动一个相当长的 运行 子进程,并希望能够用 ^C 终止它。但是,按 ^C 会导致父级接收 KeyboardInterrupt 并终止(有时会使 sleep 成为一个不存在的进程)。

import subprocess
subprocess.call("sleep 100".split())

我如何才能按下 ^C 仅终止 sleep 进程(就像我们在 shell 命令行上所做的那样),并允许父级继续?我相信我尝试了一些使用 preexec_fnstart_new_sessionshell 标志到 call 的组合,但没有成功。

Edit:我知道我可以将 subprocess 调用包装在 try-catch 块中,并忽略键盘中断;但我不想那样做。我的问题是:键盘中断应该杀死 sleep,并且应该结束它。可以说,为什么然后传播到父级。还是 sleep 进程从来都不是接收中断的进程?如果不是,我如何让它成为前台进程?

同样,我正在尝试模拟命令行的父子关系。如果我在命令行上执行相同的操作,则无需额外处理即可离开。

使用signal捕获SIGINT,并使信号处理程序终止子进程。

查看此以获取更多信息(如果它适用于 Python 2.x):

https://docs.python.org/2/library/signal.html

不确定这是否是一种解决方法,但它工作正常(至少在 Windows 上以不同方式处理 CTRL+C)

import subprocess
try:
    subprocess.call(r"C:\msys64\usr\bin\sleep 100".split())
except KeyboardInterrupt:
    print("** BREAK **")
print("continuing the python program")

执行:

K:\jff\data\python>dummy_wait.py
** BREAK **
continuing the python program

正如 Jacob 所建议的,一种方法(感谢一位同事)是处理 SIGNAL 并将其传递给 child。所以像这样的包装器将是:

import signal
import subprocess

def run_cmd(cmd, **kwargs):
    try:
        p = None

        # Register handler to pass keyboard interrupt to the subprocess
        def handler(sig, frame):
            if p:
                p.send_signal(signal.SIGINT)
            else:
                raise KeyboardInterrupt
        signal.signal(signal.SIGINT, handler)

        p = subprocess.Popen(cmd, **kwargs)
        if p.wait():
            raise Exception(cmd[0] + " failed")
    finally:
        # Reset handler
        signal.signal(signal.SIGINT, signal.SIG_DFL)