Question

J'ai un programme Python pour Linux ressemble presque à celui-ci:

import os
import time

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

time.sleep(1)

os.popen("killall top")

print process

programme se bloque dans cette ligne:

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

et qui se passe dans les outils qui maintiennent la mise à jour comme délivrer en sortie « Top »

mes meilleurs essais:

import os
import time
import subprocess

process = subprocess.Popen('top')

time.sleep(2)

os.popen("killall top")

print process

il a mieux fonctionné que le premier (il est kelled), mais il retourne:

<subprocess.Popen object at 0x97a50cc>

le second essai:

import os
import time
import subprocess

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

time.sleep(2)

os.popen("killall top")

print process

le même que le premier. Il est pendu à cause de "readlines ()"

Son retour devrait ressembler à ceci:

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

et enregistrer dans la variable « processus ». Tous les types d'idée de moi, je suis vraiment coincé maintenant?

Était-ce utile?

La solution

#!/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()

Tail-like Solutions qui impriment seule la partie de la sortie

Vous pouvez lire la sortie de processus dans un autre thread et enregistrer le nombre requis des dernières lignes dans une file d'attente:

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()

Cette variante nécessite q.append() être opération atomique. Sinon, la sortie peut-être endommagé.

signal.alarm() solution

Vous pouvez utiliser signal.alarm() pour appeler le process.terminate() après le délai spécifié au lieu de lecture dans un autre fil. Bien qu'il pourrait ne pas interagir très bien avec le module subprocess. Sur la base de réponse Martelli @ Alex:

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()

Cette approche ne fonctionne que sur les systèmes * nix. Il pourrait bloquer si process.stdout.readline() ne retourne pas.

threading.Timer solution

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()

Cette approche devrait également fonctionner sur Windows. Ici, j'ai process.stdout utilisé comme itérables; il pourrait introduire une mise en mémoire tampon de sortie supplémentaire, vous pouvez passer à l'approche iter(process.stdout.readline, "") si elle n'est pas souhaitable. si le processus ne se termine pas sur process.terminate() puis les scripts se bloque.

Pas de fils, aucune solution signaux

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()

Cette utilisation variante ni fils, aucun signal mais brouillé produit une sortie dans le terminal. Il bloque si les blocs de process.stdout.readline().

Autres conseils

Au lieu d'utiliser « top » Je suggère d'utiliser « ps » qui vous donnera les mêmes informations, mais une seule fois au lieu d'une fois par seconde pour toute l'éternité.

Vous aurez besoin d'utiliser aussi des drapeaux avec ps, je tends à utiliser « ps aux »

Qu'est-ce que je ferais, plutôt que cette approche, est d'examiner le programme que vous essayez d'obtenir des informations à partir et déterminer la source de cette information ultime. Il peut être un appel API ou nœud de périphérique. Ensuite, écrire quelques python qu'il obtient de la même source. Cela permet d'éliminer les problèmes et les frais généraux de données « grattage » « cuit ».

(J. F. Sebastian vos codes ne fonctionnent pas très bien, je pense qu'il vaut mieux que ma solution =))

Je l'ai résolu à l'aide d'une autre manière.

Au lieu de faire la sortie directement sur le terminal que je fais dans un fichier « tmp_file »:

top >> tmp_file

J'ai ensuite utilisé l'outil « coupe » pour faire sa sortie « qui est sortie supérieure » comme valeur de processus

cat tmp_file

et il a fait ce que je veux faire .C'est le code final:

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 =)

Merci les gars beaucoup pour aider

Dans les faits, si vous remplissez la mémoire tampon de sortie, vous allez finir avec une réponse. Donc, une solution consiste à remplir la mémoire tampon avec une grande quantité de déchets (~ 6000 caractères avec bufsize = 1).

Le mot Let, au lieu de haut, vous avez un script python qui écriture sur sys.stdout:

GARBAGE='.\n'
sys.stdout.write(valuable_output)
sys.stdout.write(GARBAGE*3000)

Du côté lanceur, au lieu de process.readline simple ():

GARBAGE='.\n'
line=process.readline()
while line==GARBAGE:
   line=process.readline()

Bien sûr qu'il est un sale peu comme 2000 dépend de la mise en œuvre du sous-processus, mais il fonctionne très bien et est très simple. réglage de quoi que ce soit, mais bufsize = 1 aggraver la question.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top