web-dev-qa-db-fra.com

Exécuter une commande interactive depuis python

J'ai un script que je veux exécuter depuis python (2.6.5) qui suit la logique ci-dessous:

  • Demander à l'utilisateur un mot de passe. Ressemble à ("Entrez le mot de passe:") (* Remarque: la saisie ne correspond pas à l'écran)
  • Produire des informations non pertinentes
  • Invite l'utilisateur à répondre ("Blah Blah nomfichier.txt blah blah (Y/N) ?:") 

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.

19
user1521597

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. 

10
sid16rgt

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
0
Tom Lime