【问题标题】:Terminate a gnome-terminal opened with subprocess终止使用子进程打开的 gnome 终端
【发布时间】: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 时这有点棘手,但我没想到会遇到问题。

【问题讨论】:

    标签: python terminal subprocess


    【解决方案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)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    • 2017-01-22
    相关资源
    最近更新 更多