杀死 less(1) 子进程后输入失败

Posted

技术标签:

【中文标题】杀死 less(1) 子进程后输入失败【英文标题】:Input fails after killing less(1) subprocess 【发布时间】:2012-01-25 16:35:58 【问题描述】:

我正在编写一个程序,它使用 Unix less(1) 在终端上显示文本。以下是相关部分:

less = subprocess.Popen(['less -F -'], stdin=subprocess.PIPE, 
            stdout=sys.stdout, shell=True)
try:
    less.stdin.write(rfc_text)
    less.stdin.flush()
    less.stdin = sys.stdin
    less.wait()
except IOError:
    less.terminate()
    return errno.EPIPE
except KeyboardInterrupt:
    less.terminate()
    return 0

在等待 less 完成时,我会监听 KeyboardInterrupt 异常。如果我抓住了一个,我会用 SIGTERM 信号杀死更少,然后退出我的程序。

现在,当这种情况发生时,我返回到我的 shell 提示符,但 shell 不再回显我写的内容,我必须执行 reset(1) 以使其再次工作。

关于如何在不将我的标准输入带入坟墓的情况下减少死亡的任何想法?完整源代码在https://github.com/jforberg/rfc/blob/master/rfc.py

编辑:经过一些试验,我发现 less(1) 和 man(1) 默认忽略 control-C 笔画。因此,简单地忽略它可能是一个可行的选择。我不确定我是否认为这是正确的方法,所以如果有人有建议我仍然非常感兴趣。

【问题讨论】:

作为一种廉价的解决方法,您可以在脚本末尾添加os.system('reset')。正确的解决方案可能包括礼貌地停止 less,即将一个 sigint 和 'q' 写入它的标准输入,然后等待它完成。 @wim 是您的“正确解决方案”:当然!我没有想到要少写'q'。即使经过一段时间的 Linux 编程,我仍然需要提醒我一切都是文件。请将其移至答案中,我会接受。 由于没有setpgid 技巧,less 应该与您的 Python 进程在同一个进程组中,因此它会收到与您的 Python 进程相同的 ^C。不过,我想它会尝试进行自己的特殊处理……如何使用-e (--quit-at-eof) 以便在您的程序挂起后退出? 【参考方案1】:

最简单的方法是要求用户正确退出less(按q):

#!/usr/bin/env python
from subprocess import PIPE, Popen

p = Popen(['less'], stdin=PIPE)
try:
    p.communicate(''.join("%d\n" % i for i in range(1000)))
except KeyboardInterrupt:
    print("Press `q` to exit.")
    p.wait()

【讨论】:

【参考方案2】:

基本上有两种选择:

    更少,man 和相关程序不会在 control-C 上终止,它们只是中止它们目前正在执行的任何操作。因此,简单地忽略击键似乎是一种选择。这将保留有用的功能,即可以在不终止程序的情况下中止冗长的操作,例如长时间的搜索。还希望程序能够像类似的程序(在本例中为 Unix man)一样运行。

    Less 接受 -K 选项,这将使其在 control-C 时终止,就像大多数非交互式命令一样。

【讨论】:

【参考方案3】:

啊啊! J.F. Sebastian 解决方案很好,但并非在所有情况下都有效。在我的应用程序中,我想使用像“tail -f”这样的“less”,它是通过“less +F -S logfilename”完成的。用户必须按CTRL+C进入普通模式,按Q退出。

为了让这个过程顺利进行,我提出了以下 6 行解决方案(基于 J.F. Sebastian 代码):

logpath = os.path.join(cwd, "filename.log")
less = Popen(["less","+F","-S", logpath])
retcode = None
while retcode is None:
    try: retcode = less.wait()
    except KeyboardInterrupt: pass

我认为这解决了原来的问题。如果您输入的内容更少,您可能需要调整使用的 Popen 方法:wait()communicate(input)

【讨论】:

以上是关于杀死 less(1) 子进程后输入失败的主要内容,如果未能解决你的问题,请参考以下文章

Python子进程,定时延迟后杀死进程

杀死子进程

如何使用子进程模块杀死(或避免)僵尸进程

C# - 向子进程发送输入 - 控制台应用程序

systemd `systemctl stop` 主动杀死子进程

当父进程被杀死时,使用 fork() 创建的子进程是不是会自动被杀死?