gpt4 book ai didi

python - 停止在 Python 中读取进程输出而不挂起?

转载 作者:太空狗 更新时间:2023-10-29 17:13:05 24 4
gpt4 key购买 nike

我有一个 Linux 的 Python 程序几乎看起来像这样:

import os
import time

process = os.popen("top").readlines()

time.sleep(1)

os.popen("killall top")

print process

程序卡在这一行:

process = os.popen("top").readlines()

这发生在保持更新输出如“Top”的工具中

我最好的尝试:

import os
import time
import subprocess

process = subprocess.Popen('top')

time.sleep(2)

os.popen("killall top")

print process

它比第一个效果更好(它被 kelled ),但它返回:

<subprocess.Popen object at 0x97a50cc>

二审:

import os
import time
import subprocess

process = subprocess.Popen('top').readlines()

time.sleep(2)

os.popen("killall top")

print process

与第一个相同。由于“readlines()”而挂起

它的返回应该是这样的:

top - 05:31:15 up 12:12,  5 users,  load average: 0.25, 0.14, 0.11
Tasks: 174 total, 2 running, 172 sleeping, 0 stopped, 0 zombie
Cpu(s): 9.3%us, 3.8%sy, 0.1%ni, 85.9%id, 0.9%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 1992828k total, 1849456k used, 143372k free, 233048k buffers
Swap: 4602876k total, 0k used, 4602876k free, 1122780k cached

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
31735 Barakat 20 0 246m 52m 20m S 19.4 2.7 13:54.91 totem
1907 root 20 0 91264 45m 15m S 1.9 2.3 38:54.14 Xorg
2138 Barakat 20 0 17356 5368 4284 S 1.9 0.3 3:00.15 at-spi-registry
2164 Barakat 9 -11 164m 7372 6252 S 1.9 0.4 2:54.58 pulseaudio
2394 Barakat 20 0 27212 9792 8256 S 1.9 0.5 6:01.48 multiload-apple
6498 Barakat 20 0 56364 30m 18m S 1.9 1.6 0:03.38 pyshell
1 root 20 0 2880 1416 1208 S 0.0 0.1 0:02.02 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd
3 root RT 0 0 0 0 S 0.0 0.0 0:00.12 migration/0
4 root 20 0 0 0 0 S 0.0 0.0 0:02.07 ksoftirqd/0
5 root RT 0 0 0 0 S 0.0 0.0 0:00.00 watchdog/0
9 root 20 0 0 0 0 S 0.0 0.0 0:01.43 events/0
11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuset
12 root 20 0 0 0 0 S 0.0 0.0 0:00.02 khelper
13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 netns
14 root 20 0 0 0 0 S 0.0 0.0 0:00.00 async/mgr
15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 pm

并保存在变量“process”中。伙计们,我真的被困住了吗?

最佳答案

#!/usr/bin/env python
"""Start process; wait 2 seconds; kill the process; print all process output."""
import subprocess
import tempfile
import time

def main():
# open temporary file (it automatically deleted when it is closed)
# `Popen` requires `f.fileno()` so `SpooledTemporaryFile` adds nothing here
f = tempfile.TemporaryFile()

# start process, redirect stdout
p = subprocess.Popen(["top"], stdout=f)

# wait 2 seconds
time.sleep(2)

# kill process
#NOTE: if it doesn't kill the process then `p.wait()` blocks forever
p.terminate()
p.wait() # wait for the process to terminate otherwise the output is garbled

# print saved output
f.seek(0) # rewind to the beginning of the file
print f.read(),
f.close()

if __name__=="__main__":
main()

只打印部分输出的类似尾部的解决方案

您可以在另一个线程中读取进程输出并将所需数量的最后一行保存在队列中:

import collections
import subprocess
import time
import threading

def read_output(process, append):
for line in iter(process.stdout.readline, ""):
append(line)

def main():
# start process, redirect stdout
process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)
try:
# save last `number_of_lines` lines of the process output
number_of_lines = 200
q = collections.deque(maxlen=number_of_lines) # atomic .append()
t = threading.Thread(target=read_output, args=(process, q.append))
t.daemon = True
t.start()

#
time.sleep(2)
finally:
process.terminate() #NOTE: it doesn't ensure the process termination

# print saved lines
print ''.join(q)

if __name__=="__main__":
main()

此变体要求 q.append() 是原子操作。否则输出可能会损坏。

signal.alarm()解决方案

你可以使用 signal.alarm()在指定超时后调用 process.terminate() 而不是在另一个线程中读取。虽然它可能不会与 subprocess 模块很好地交互。基于@Alex Martelli's answer :

import collections
import signal
import subprocess

class Alarm(Exception):
pass

def alarm_handler(signum, frame):
raise Alarm

def main():
# start process, redirect stdout
process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)

# set signal handler
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(2) # produce SIGALRM in 2 seconds

try:
# save last `number_of_lines` lines of the process output
number_of_lines = 200
q = collections.deque(maxlen=number_of_lines)
for line in iter(process.stdout.readline, ""):
q.append(line)
signal.alarm(0) # cancel alarm
except Alarm:
process.terminate()
finally:
# print saved lines
print ''.join(q)

if __name__=="__main__":
main()

此方法仅适用于 *nix 系统。如果 process.stdout.readline() 没有返回,它可能会阻塞。

threading.Timer解决方案

import collections
import subprocess
import threading

def main():
# start process, redirect stdout
process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True)

# terminate process in timeout seconds
timeout = 2 # seconds
timer = threading.Timer(timeout, process.terminate)
timer.start()

# save last `number_of_lines` lines of the process output
number_of_lines = 200
q = collections.deque(process.stdout, maxlen=number_of_lines)
timer.cancel()

# print saved lines
print ''.join(q),

if __name__=="__main__":
main()

这种方法也适用于 Windows。在这里,我使用了 process.stdout 作为可迭代对象;它可能会引入额外的输出缓冲,如果不需要,您可以切换到 iter(process.stdout.readline, "") 方法。如果进程未在 process.terminate() 上终止,则脚本将挂起。

无线程,无信号解决方案

import collections
import subprocess
import sys
import time

def main():
args = sys.argv[1:]
if not args:
args = ['top']

# start process, redirect stdout
process = subprocess.Popen(args, stdout=subprocess.PIPE, close_fds=True)

# save last `number_of_lines` lines of the process output
number_of_lines = 200
q = collections.deque(maxlen=number_of_lines)

timeout = 2 # seconds
now = start = time.time()
while (now - start) < timeout:
line = process.stdout.readline()
if not line:
break
q.append(line)
now = time.time()
else: # on timeout
process.terminate()

# print saved lines
print ''.join(q),

if __name__=="__main__":
main()

此变体既不使用线程,也不使用信号,但它会在终端中产生乱码输出。如果 process.stdout.readline() 阻塞,它将阻塞。

关于python - 停止在 Python 中读取进程输出而不挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4417962/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com