【问题标题】:cannot kill a Sub process created by Popen when printing process.stdout打印 process.stdout 时无法终止 Popen 创建的子进程
【发布时间】:2015-03-22 04:17:02
【问题描述】:

我创建了一个脚本,它应该运行一个命令并在 15 秒后终止它

import logging

import subprocess
import time
import os
import sys
import signal

#cmd = "ping 192.168.1.1 -t"
cmd = "C:\\MyAPP\MyExe.exe -t 80 -I C:\MyApp\Temp -M Documents"

proc=subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,shell=True)

**for line in proc.stdout:
    print (line.decode("utf-8"), end='')**

time.sleep(15)
os.kill(proc.pid, signal.SIGTERM)
#proc.kill() #Tried this too but no luck 

这不会终止我的子进程。但是,如果我将日志记录注释到标准输出部分,即

for line in proc.stdout:
    print (line.decode("utf-8"), end='')

子进程已被杀死。

我也试过proc.kill() and CTRL_C_EVENT,但没有运气。 任何帮助将不胜感激。请把我看成是python的新手

【问题讨论】:

标签: python windows subprocess


【解决方案1】:

在 15 秒内终止子进程,同时逐行打印其输出:

#!/usr/bin/env python
from __future__ import print_function
from threading import Timer
from subprocess import Popen, PIPE, STDOUT

# start process
cmd = r"C:\MyAPP\MyExe.exe -t 80 -I C:\MyApp\Temp -M Documents"
process = Popen(cmd, stdout=PIPE, stderr=STDOUT,
                bufsize=1, universal_newlines=True)

# terminate process in 15 seconds
timer = Timer(15, terminate, args=[process])
timer.start()

# print output
for line in iter(process.stdout.readline, ''):
    print(line, end='')
process.stdout.close()
process.wait() # wait for the child process to finish
timer.cancel()

注意,这里不需要shell=True。您可以将terminate() 定义为:

def terminate(process):
    if process.poll() is None:
        try:
            process.terminate()
        except EnvironmentError:
            pass # ignore 

如果你想杀死整个进程树,那么将terminate()定义为:

from subprocess import call

def terminate(process):
    if process.poll() is None:
        call('taskkill /F /T /PID ' + str(process.pid))
  1. 对 Windows 路径使用原始字符串文字:r"" 否则您应该转义字符串文字中的所有反斜杠
  2. 删除shell=True。它在这里无缘无故地创建了一个额外的过程
  3. universal_newlines=True 启用文本模式(在 Python 3 上自动使用语言环境首选编码将字节解码为 Unicode 文本)
  4. iter(process.stdout.readline, '') 是与 Python 2 兼容所必需的(否则,由于预读缓冲区错误,打印数据可能会延迟)
  5. 使用process.terminate() 代替process.send_signal(signal.SIGTERM)os.kill(proc.pid, signal.SIGTERM)
  6. taskkill 允许在 Windows 上杀死进程树

【讨论】:

    【解决方案2】:

    问题是从标准输出读取被阻塞。您需要读取子进程的输出或在单独的线程上运行计时器。

    from subprocess import Popen, PIPE
    from threading import Thread
    from time import sleep
    
    class ProcKiller(Thread):
        def __init__(self, proc, time_limit):
            super(ProcKiller, self).__init__()
            self.proc = proc
            self.time_limit = time_limit
    
        def run(self):
            sleep(self.time_limit)
            self.proc.kill()
    
    
    p = Popen('while true; do echo hi; sleep 1; done', shell=True)
    t = ProcKiller(p, 5)
    t.start()
    p.communicate()
    

    已编辑以反映评论中的建议更改

    from subprocess import Popen, PIPE
    from threading import Thread
    from time import sleep
    from signal import SIGTERM
    import os
    
    class ProcKiller(Thread):
        def __init__(self, proc, time_limit):
            super(ProcKiller, self).__init__()
            self.proc = proc
            self.time_limit = time_limit
    
        def run(self):
            sleep(self.time_limit)
            os.kill(self.proc.pid, SIGTERM)
    
    p = Popen('while true; do echo hi; sleep 1; done', shell=True)
    t = ProcKiller(p, 5)
    t.start()
    p.communicate()
    

    【讨论】:

    • 感谢您的回复。在 time_limit 之后焦点返回到主窗口,但我看到该进程仍在后台运行,其输出仍打印在标准输出中。除非完成或我发送 CTL+C,否则该进程永远不会终止
    • 你可以尝试运行 "os.kill(self.proc.pid, signal.SIGTERM)" 而不是 "self.proc.kill()" 然后
    • 不幸的是仍然面临同样的效果。子进程没有被杀死。 PS:我使用的是 Windows 64 位
    • 根据这篇文章stackoverflow.com/questions/696345/…,您可能希望在没有 shell=True 的情况下运行子进程,它的语法略有不同。使用 shlex.split() 来保存一些计算:docs.python.org/2/library/subprocess.html#popen-constructor
    • 你不需要自定义 Thread 子类,there is threading.Timer already
    猜你喜欢
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    相关资源
    最近更新 更多