终止使用子进程打开的 gnome 终端

Posted

技术标签:

【中文标题】终止使用子进程打开的 gnome 终端【英文标题】:Terminate a gnome-terminal opened with subprocess 【发布时间】:2016-04-12 02:18:36 【问题描述】:

使用 subprocess 和命令 'gnome-terminal -e bash' 我可以根据需要打开一个 gnome 终端(并让它一直存在)。这是通过任一方式完成的

p=subprocess.Popen(['gnome-terminal', '-e', 'bash'])

p=subprocess.Popen(['gnome-terminal -e bash'], shell=True)

但我无法使用p.terminate()p.kill() 关闭终端。据我了解,使用shell=True 时这有点棘手,但我没想到会遇到问题。

【问题讨论】:

【参考方案1】:

终止终端及其子进程(在同一进程组中):

#!/usr/bin/env python
import os
import signal
import subprocess

p = subprocess.Popen(['gnome-terminal', '--disable-factory', '-e', 'bash'],
                     preexec_fn=os.setpgrp)
# do something here...
os.killpg(p.pid, signal.SIGINT)
--disable-factory 用于避免重复使用活动终端,以便我们可以通过 subprocess 句柄杀死新创建的终端 os.setpgrpgnome-terminal 放在自己的进程组中,以便os.killpg() 可用于向该组发送信号

【讨论】:

没有--disable-factory,有没有办法解决这个问题?我正在尝试使这个可选地与 xterm 一起使用,它没有 --disable-factory 选项。 @Shatnerz:我不知道xterm 是如何工作的。您可以专门针对 xterm 案例提出单独的 Stack Overflow 问题。 只是一个更新:--disable-factory 不再受支持,至少从版本 3.28.2 开始。我仍然对这个问题的答案感兴趣,因为这个解决方案对我不起作用! @JamesPaulMason:答案中的代码在我的带有 GNOME Terminal 3.28.2 的 Ubuntu 机器上工作。 刚刚在两个不同的系统上试过。它在带有 gnome-terminal 3.18.3 的 Ubuntu 上工作,但在使用 VTE 0.52.2 +GNUTLS 的带有 gnome-terminal 3.28.2 的 RedHat 上不工作。 ¯_(ツ)_/¯【参考方案2】:

您应该能够执行此解决方法:

获取进程ID 杀死进程

工作解决方案:关闭 gnome-terminal-server

正如@j-f-sebastian 在评论中所建议的那样,gnome-terminal

只需发送请求(到gnome-terminal-server)以启动一个新终端并立即退出——没有什么可以杀死进程已经死了(并且新创建的进程不是后代:新的bash 进程是gnome-terminal-server 的孩子,而不是 gnome-terminal)。

import subprocess
import os, signal
import time

p=subprocess.Popen(['gnome-terminal -e bash'], stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
print "this is going to be closed in 3 sec"
time.sleep(3)
# this line returns the list of bash instances pid as string
bash_pids = subprocess.check_output(["pidof", "bash"])
# I get the last instance opened
pid_to_kill = bash_pids.split(" ")[0]
os.kill(int(pid_to_kill), signal.SIGTERM)

我的解决方案是遵循这个逻辑:

运行 gnome-terminal 获取最新的bash实例打开的进程id 杀死这个进程ID

无效的解决方案

这些解决方案可能适用于更简单的情况:

解决方案 1

import subprocess
import os, signal

p=subprocess.Popen(['gnome-terminal -e bash'], shell=True)
p_pid = p.pid  # get the process id
os.kill(p_pid, signal.SIGKILL)

为了选择适当的信号传递方法而不是SIGKILL,您可以参考signal documentation。例如

在 Windows 上,signal() 只能使用 SIGABRT、SIGFPE、SIGILL、SIGINT、SIGSEGV 或 SIGTERM 调用

对于 Unix,你有一个相当广泛的方法列表来调用。

要更好地了解os.kill,您可以参考its documentation。

解决方案 2

对 Unix 有用的另一种方法是:

import subprocess
import os, signal

p=subprocess.Popen(['gnome-terminal -e bash'], stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), signal.SIGTERM)

您的进程似乎正在打开阻止父进程关闭的子进程。将session id 添加到您的父进程中,您应该能够修复它。

解决方案 3

import subprocess, psutil

def kill(p_pid):
    process = psutil.Process(p_pid)
    for proc in process.get_children(recursive=True):
        proc.kill()
    process.kill()

p = subprocess.Popen(['gnome-terminal -e bash'], shell=True)
try:
    p.wait(timeout=3)
except subprocess.TimeoutExpired:
    kill(p.pid)

此解决方案需要psutil

解决方案 4

根据askubuntu,似乎关闭 gnome 终端实例的最佳方法是执行 bash 命令,例如:

killall -s signal gnome-terminal

其中 signal 模拟 Alt + F4。

您可以尝试使用 [pexpect]:

p = pexpect.spawn(your_cmd_here)
p.send('^F4')

【讨论】:

我完全按照您在 ipython 终端中所说的进行了尝试。打开的终端没有关闭。 我想我找到了。我编辑了答案,添加了会话 ID 并使用 os.killpg 而不是 os.kill 关闭 所以,这看起来像是进步。我可以准确地复制和粘贴代码。没有终端弹出。我认为它会立即被销毁。但是,我添加了 3 秒的睡眠,并且终端永远不会被破坏,即使在脚本完成时也是如此。当我尝试使用交互式 python shell 时,似乎什么也没发生 另一种选择可能是使用psutil。我会在答案中再写一个例子,你可以试一试! 投反对票。没有一个解决方案适用于gnome-terminal。在我的系统上gnome-terminal 是一个启动应用程序,它不等待实际终端退出:它只是发送请求(到gnome-terminal-server)以启动一个新终端并立即退出——没有什么可以杀死进程是已经死了(并且新创建的进程不是后代:新的bash 进程是gnome-terminal-server 的子进程,而不是gnome-terminal)。

以上是关于终止使用子进程打开的 gnome 终端的主要内容,如果未能解决你的问题,请参考以下文章

无法在 Windows 上使用 Python 终止正在运行的子进程

python子进程终端mac osx

后台执行进程的方法

setsid()

如何在父进程终止后终止所有子进程?

进程控制---子进程终止状态相关的宏