Stopp Prozessausgabe in Python ohne hängen zu lesen?
-
08-10-2019 - |
Frage
Ich habe ein Python-Programm für Linux sieht fast aus wie diese:
import os
import time
process = os.popen("top").readlines()
time.sleep(1)
os.popen("killall top")
print process
das Programm hängt in dieser Zeile:
process = os.popen("top").readlines()
und das geschieht in den Werkzeugen, die Update-Ausgabe halten wie "Top"
meine besten Studien:
import os
import time
import subprocess
process = subprocess.Popen('top')
time.sleep(2)
os.popen("killall top")
print process
es funktionierte besser als die erste (es ist kelled), aber es gibt:
<subprocess.Popen object at 0x97a50cc>
der zweite Versuch:
import os
import time
import subprocess
process = subprocess.Popen('top').readlines()
time.sleep(2)
os.popen("killall top")
print process
die gleiche wie die erste. Es gehängt wegen "readlines ()"
Die Rückkehr sollte wie folgt sein:
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
und speichern in der Variablen „Prozess“. Irgendwelche I Idee Jungs, ich bin jetzt wirklich fest?
Lösung
#!/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()
schweifartigen Lösungen, die nur den Teil der Ausgabe
druckenSie könnten den Prozessausgang in einem anderen Thread lesen und die erforderliche Anzahl von den letzten Zeilen in einer Warteschlange speichern:
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()
Diese Variante erfordert q.append()
atomare Operation zu sein. Andernfalls könnte der Ausgang beschädigt werden.
signal.alarm()
Lösung
könnten Sie verwenden signal.alarm()
die process.terminate()
nach bestimmten Timeout zu rufen, anstatt die in einem anderen Thread zu lesen. Obwohl es in Wechselwirkung tritt vielleicht nicht sehr gut mit dem subprocess
Modul. Basierend auf @ Alex Martelli Antwort :
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()
Dieser Ansatz funktioniert nur auf * nix-Systemen. Es könnte sperren, wenn process.stdout.readline()
nicht zurück.
threading.Timer
Lösung
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()
Dieser Ansatz sollte auch unter Windows arbeiten. Hier habe ich gebraucht process.stdout
als iterable haben; es könnte eine zusätzliche Ausgabepufferung einführen, können Sie zum iter(process.stdout.readline, "")
Ansatz wechseln könnte, wenn es nicht erwünscht ist. wenn der Prozess nicht beendet, auf process.terminate()
dann der Skripts hängt.
Keine Fäden, keine Signale Lösung
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()
Diese Variante Gebrauch weder Fäden, keine Signale, aber es erzeugt eine Ausgabe im Terminal verstümmelt. Es wird blockiert, wenn process.stdout.readline()
Blöcke.
Andere Tipps
Anstelle von „oben“ Ich schlage vor, mit „ps“ verwendet, die geben Ihnen die gleichen Informationen, aber nur einmal statt einmal pro Sekunde für alle Ewigkeit.
Sie müssen auch einige Fahnen mit ps verwenden, neige ich dazu, „ps aux“
zu verwenden,Was ich tun würde, anstatt dieser Ansatz ist das Programm prüfen Sie versuchen, Informationen aus und bestimmen die ultimative Quelle dieser Informationen zu erhalten. Es kann ein API-Aufruf oder Geräteknoten sein. Dann schreiben Sie einige Python, die es aus der gleichen Quelle erhält. Das eliminiert „die Probleme und Overhead„Schaben““ Daten gekocht.
(J. F. Sebastian Ihre Codes lassen sich gut, ich denke, es ist besser als meine Lösung =))
Ich habe es gelöst andere Art und Weise verwendet wird.
Statt den Ausgang direkt am Terminal macht ich es in eine Datei „tmp_file“ machen:
top >> tmp_file
Dann habe ich das Tool „cut“ seinen Ausgang zu machen „die Top-Ausgang ist“ als Prozesswert
cat tmp_file
und es tat, was ich will es tun .Diese der endgültige Code lautet:
import os
import subprocess
import time
subprocess.Popen("top >> tmp_file",shell = True)
time.sleep(1)
os.popen("killall top")
process = os.popen("cat tmp_file").read()
os.popen("rm tmp_file")
print process
# Thing better than nothing =)
Vielen Dank Jungs für Hilfe
Fakten, wenn Sie den Ausgabepuffer zu füllen, werden Sie mit etwas Antwort beenden. So eine Lösung ist, die Puffer mit einem großen Müll Ausgang (~ 6000 Zeichen mit bufsize = 1).
füllenLassen Sie uns sagen, statt oben, haben Sie ein Python-Skript, das schreiben auf sys.stdout:
GARBAGE='.\n'
sys.stdout.write(valuable_output)
sys.stdout.write(GARBAGE*3000)
Auf der Abschuss Seite, statt einfach process.readline ():
GARBAGE='.\n'
line=process.readline()
while line==GARBAGE:
line=process.readline()
Ganz sicher ist es ein bisschen schmutzig, wie 2000 über subprocess Umsetzung abhängig ist, aber es funktioniert gut und ist sehr einfach. Einstellung alles andere als BUFSIZE = 1 die Sache noch schlimmer zu machen.