J'ai un script que je veux exécuter depuis python (2.6.5) qui suit la logique ci-dessous:
La dernière ligne d'invite contient du texte que je dois analyser (filename.txt). La réponse fournie n'a pas d'importance (le programme peut en réalité quitter ici sans en fournir un, tant que je peux analyser la ligne)
Mes exigences sont un peu similaires à Emballer une application interactive en ligne de commande dans un script python , mais les réponses semblent un peu déroutantes, et les miennes restent bloquées même lorsque le PO indique que ce n'est pas pour lui .
En regardant autour de moi, je suis arrivé à la conclusion que subprocess
est la meilleure façon de procéder, mais quelques problèmes me manquent. Voici ma ligne Popen:
p = subprocess.Popen("cmd", Shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
Lorsque j'appelle une read()
ou readline()
sur stdout
, l'invite s'affiche à l'écran et se bloque.
Si j'appelle une write("password\n")
pour stdin
, l'invite est écrite à l'écran et se bloque. Le texte dans write()
n'est pas écrit (je ne déplace pas le curseur pour déplacer une nouvelle ligne).
Si j'appelle p.communicate("password\n")
, même comportement que write ()
Je cherchais ici quelques idées sur la meilleure façon de saisir stdin
et éventuellement d’analyser la dernière ligne de la sortie si vous vous sentiez généreux, même si je pourrais probablement le comprendre un jour.
Si vous communiquez avec un programme qui génère un sous-processus, vous devez extraire lire sans blocage sur un sous-processus.PIPE en python . J'ai eu un problème similaire avec mon application et l'utilisation de files d'attente est le meilleur moyen de communiquer en permanence avec un sous-processus.
Pour obtenir des valeurs de l'utilisateur, vous pouvez toujours utiliser la commande raw_input () pour obtenir des réponses. Pour les mots de passe, essayez d'utiliser le module getpass
pour obtenir les mots de passe sans écho de votre utilisateur. Vous pouvez ensuite analyser ces réponses et les écrire dans le stdin de votre sous-processus.
J'ai fini par faire quelque chose qui ressemble à ce qui suit:
import sys
import subprocess
from threading import Thread
try:
from Queue import Queue, Empty
except ImportError:
from queue import Queue, Empty # python 3.x
def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()
def getOutput(outQueue):
outStr = ''
try:
while True: #Adds output from the Queue until it is empty
outStr+=outQueue.get_nowait()
except Empty:
return outStr
p = subprocess.Popen("cmd", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, Shell=False, universal_newlines=True)
outQueue = Queue()
errQueue = Queue()
outThread = Thread(target=enqueue_output, args=(p.stdout, outQueue))
errThread = Thread(target=enqueue_output, args=(p.stderr, errQueue))
outThread.daemon = True
errThread.daemon = True
outThread.start()
errThread.start()
try:
someInput = raw_input("Input: ")
except NameError:
someInput = input("Input: ")
p.stdin.write(someInput)
errors = getOutput(errQueue)
output = getOutput(outQueue)
Une fois les files d'attente créées et les threads démarrés, vous pouvez obtenir les entrées de l'utilisateur, obtenir les erreurs et les résultats du processus, puis les traiter et les afficher à l'utilisateur.
L'utilisation de threading peut être légèrement excessive pour des tâches simples . À la place, os.spawnvpe peut être utilisé Cela engendrera le script Shell en tant que processus. Vous pourrez communiquer de manière interactive avec le script. Dans cet exemple, j'ai passé le mot de passe en argument, ce n'est évidemment pas une bonne idée.
import os
import sys
from getpass import unix_getpass
def cmd(cmd):
cmd = cmd.split()
code = os.spawnvpe(os.P_WAIT, cmd[0], cmd, os.environ)
if code == 127:
sys.stderr.write('{0}: command not found\n'.format(cmd[0]))
return code
password = unix_getpass('Password: ')
cmd_run = './run.sh --password {0}'.format(password)
cmd(cmd_run)
pattern = raw_input('Pattern: ')
lines = []
with open('filename.txt', 'r') as fd:
for line in fd:
if pattern in line:
lines.append(line)
# manipulate lines