2009-07-14 4 views
7

J'utilise python 2.5 sous Windows. Je souhaite interagir avec un processus de console via Popen. J'ai actuellement ce petit bout de code:Interagir avec une application de console Windows via Python

p = Popen(["console_app.exe"], stdin=PIPE, stdout=PIPE) 
# issue command 1... 
p.stdin.write('command1\n') 
result1 = p.stdout.read() # <---- we never return here 
# issue command 2... 
p.stdin.write('command2\n') 
result2 = p.stdout.read() 

Je peux écrire à stdin mais ne peut pas lire stdout. Ai-je manqué une étape? Je ne veux pas utiliser p.communicate ("command") [0] car il termine le processus et j'ai besoin d'interagir avec le processus de façon dynamique au fil du temps.

Merci d'avance.

+0

Votre code semble correct. Est-ce que la console_app fonctionne bien lorsque vous exécutez à partir de la console? Que retourne-t-il pour command1? – luc

+0

yes console_app fonctionne normalement lors de l'exécution dans cmd.exe Il sort juste quelques chiffres en fonction de l'entrée fournie (et parfois des chaînes) – QAZ

Répondre

0

Je pense que vous pourriez vouloir utiliser readline() à la place?

Édition: désolé, mal interprété.

Peut-être this question peut vous aider?

+0

readline() se bloque aussi. Je peux confirmer que l'application console affiche des données qui devraient être lues correctement du côté python. – QAZ

0

Est-il possible que l'application de console mette en tampon sa sortie d'une manière ou d'une autre afin qu'elle soit uniquement envoyée à stdout lorsque le canal est fermé? Si vous avez accès au code de l'application de console, peut-être coller un flush après un lot de données de sortie peut-être aider?

Alternativement, est-ce qu'il écrit réellement à stderr et à la place de stdout pour une raison quelconque? Je viens de regarder à nouveau votre code et j'ai pensé à autre chose, je vois que vous envoyez "commande \ n". L'application console peut-elle simplement attendre un caractère de retour chariot au lieu d'une nouvelle ligne? Peut-être que l'application console attend que vous soumettiez la commande avant qu'elle ne produise une sortie.

+0

Merci, bonnes suggestions. Je n'ai malheureusement pas accès au code source de l'application console. Confirmé son écriture à stdout et pas stderr. – QAZ

0

Avait exactement le même problème ici. J'ai creusé dans le code source de DrPython et j'ai volé la solution wx.Execute(), qui fonctionne bien, surtout si votre script utilise déjà wx. Je n'ai jamais trouvé la bonne solution sur la plate-forme de Windows si ...

7

Votre problème ici est que vous essayez de contrôler une application interactive.

stdout.read() continuera à lire jusqu'à ce qu'il ait atteint la fin du flux, du fichier ou du canal. Malheureusement, dans le cas d'un programme interactif, le tuyau n'est fermé que lorsque le programme se termine; ce qui n'est jamais, si la commande que vous avez envoyée était autre que "quit".

Vous devrez revenir à lire la sortie du sous-processus ligne par ligne en utilisant stdout.readline(), et vous feriez mieux d'avoir un moyen de dire quand le programme est prêt à accepter une commande, et quand la commande que vous avez émise au programme est terminé et vous pouvez en fournir un nouveau. Dans le cas d'un programme comme cmd.exe, même readline() ne suffira pas car la ligne qui indique qu'une nouvelle commande peut être envoyée n'est pas terminée par une nouvelle ligne, donc devra analyser la sortie octet par octet. Voici un exemple de script qui va cmd.exe, cherche l'invite, puis émet un dir puis un exit: Si le timing est pas important, et l'interactivité pour un utilisateur n'est pas nécessaire

from subprocess import * 
import re 

class InteractiveCommand: 
    def __init__(self, process, prompt): 
     self.process = process 
     self.prompt = prompt 
     self.output = "" 
     self.wait_for_prompt() 

    def wait_for_prompt(self): 
     while not self.prompt.search(self.output): 
      c = self.process.stdout.read(1) 
      if c == "": 
       break 
      self.output += c 

     # Now we're at a prompt; clear the output buffer and return its contents 
     tmp = self.output 
     self.output = "" 
     return tmp 

    def command(self, command): 
     self.process.stdin.write(command + "\n") 
     return self.wait_for_prompt() 

p  = Popen(["cmd.exe"], stdin=PIPE, stdout=PIPE) 
prompt = re.compile(r"^C:\\.*>", re.M) 
cmd = InteractiveCommand(p, prompt) 

listing = cmd.command("dir") 
cmd.command("exit") 

print listing 

, il peut être beaucoup plus simple juste pour recharger les appels:

from subprocess import * 

p = Popen(["cmd.exe"], stdin=PIPE, stdout=PIPE) 
p.stdin.write("dir\n") 
p.stdin.write("exit\n") 

print p.stdout.read() 
+1

Il ne fonctionnera pas sous windows car .lead (1) bloque l'opération –

+0

Je sais, c'est pourquoi après chaque octet le script vérifie si l'invite est disponible et écrit la nouvelle commande avant de lire d'autres octets. Avez-vous essayé le script? Ça marche. – rix0rrr

+0

À droite, je n'ai pas remarqué cela à première vue. +1 pour bien attendre la mise en œuvre. –

2

Avez-vous essayé de forcer les lignes d'extrémité des fenêtres? , c'est-à-dire

p.stdin.write('command1 \r\n') 
p.stdout.readline() 

MISE À JOUR:

Je viens de vérifier la solution sur les fenêtres cmd.exe et il fonctionne avec readline(). Mais il a un problème stdout de Popen.readline bloque. Donc, si l'application va jamais retourner quelque chose sans fin de ligne, votre application sera bloquée pour toujours.

Mais il y a un travail autour de ce Vérifions: http://code.activestate.com/recipes/440554/

Questions connexes