2009-02-18 9 views
4

J'ai un script python qui doit exécuter plusieurs utilitaires de ligne de commande. La sortie stdout est parfois utilisée pour un traitement ultérieur. Dans tous les cas, je veux enregistrer les résultats et déclencher une exception si une erreur est détectée. J'utilise la fonction suivante pour y parvenir:J'ai besoin d'un meilleur moyen d'exécuter des commandes de console à partir de python et de consigner les résultats

def execute(cmd, logsink): 
    logsink.log("executing: %s\n" % cmd) 
    popen_obj = subprocess.Popen(\ 
      cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
    (stdout, stderr) = popen_obj.communicate() 
    returncode = popen_obj.returncode 
    if (returncode <> 0): 
     logsink.log(" RETURN CODE: %s\n" % str(returncode)) 
    if (len(stdout.strip()) > 0): 
     logsink.log(" STDOUT:\n%s\n" % stdout) 
    if (len(stderr.strip()) > 0): 
     logsink.log(" STDERR:\n%s\n" % stderr) 
    if (returncode <> 0): 
     raise Exception, "execute failed with error output:\n%s" % stderr 
    return stdout 

« logsink » peut être un objet python avec une méthode de journal. Je l'utilise généralement pour transmettre les données de journalisation à un fichier spécifique, ou l'écho à la console, ou les deux, ou autre chose ...

Cela fonctionne très bien, sauf pour trois problèmes où j'ai besoin de plus de grain le contrôle de la communication() méthode prévoit:

  1. stdout et stderr peuvent être intercalés sur la console , mais la fonction ci-dessus se connecte séparément. Cela peut compliquer l'interprétation du journal . Comment puis-je journaliser les lignes stdout et stderr entrelacées, dans le même ordre que lorsqu'elles ont été générées?
  2. La fonction ci-dessus enregistre uniquement la sortie de la commande une fois que la commande a terminé . Cela complique le diagnostic des problèmes lorsque les commandes sont bloquées dans une boucle infinie ou prennent beaucoup de temps pour une autre raison. Comment puis-je obtenir le journal en temps réel, alors que la commande est toujours en cours d'exécution?
  3. Si les journaux sont volumineux, il peut être difficile d'interpréter quelle commande a généré quelle sortie. Y a-t-il une façon de préfixer chaque ligne avec quelque chose (par exemple le premier mot de la chaîne cmd suivie de :).
+0

Quel est le problème avec le package de journalisation. Qu'est-ce que tu n'utilises pas? –

+0

@ S.Lott: la fonction d'exécution a juste besoin de quelque chose pour envoyer la sortie. Utiliser un objet logging.Logger compliquerait les choses car alors la fonction execute devrait savoir à quel niveau se connecter (debug, info ... warning ...). Ces préoccupations sont en dehors de la responsabilité de la fonction d'exécution. –

+0

Félicitations! –

Répondre

5

Vous pouvez rediriger vers un fichier si vous souhaitez simplement obtenir la sortie dans un fichier pour une évaluation ultérieure.

Vous définissez déjà le stdout/stderr des processus que vous exécutez par les méthodes stdout =/stderr =.

Dans votre exemple de code, vous redirigez simplement vers les affectations de sortie/d'erreur actuelles des scripts. Sys.stdout et sys.stderr ne sont que des objets de type fichier. Sys.stdout et sys.stderr Comme la documentation de la documentation sur sys.stdout mentionne, "Tout objet est acceptable tant qu'il a une méthode write() qui prend un argument de chaîne." Il suffit donc de lui donner une classe avec une méthode d'écriture afin de rediriger la sortie.

Si vous souhaitez que la sortie de la console et la sortie de fichier créent une classe pour gérer la sortie.

redirection générale:

# Redirecting stdout and stderr to a file 
f = open('log.txt', 'w') 
sys.stdout = f 
sys.stderr = f 

Faire une classe de redirection:

# redirecting to both 
class OutputManager: 
    def __init__(self, filename, console): 
     self.f = open(filename, 'w') 
     self.con = console 

    def write(self, data): 
     self.con.write(data) 
     self.f.write(data) 

new_stdout = OutputManager("log.txt", sys.stdout) 

entrelacée dépend de mise en mémoire tampon, de sorte que vous pouvez ou ne pouvez pas obtenir la sortie que vous attendez. (Vous pouvez probablement désactiver ou réduire la mise en mémoire tampon utilisé, mais je ne me souviens pas comment le moment)

+0

os.write (self.f.fileno(), data) n'est pas tamponné si vous avez besoin d'être sûr –

+0

+1 Je n'avais pas réalisé que le typage de canard python pouvait être exploité ici en fournissant ma propre implémentation d'objet File. Je vais essayer et je publierai une fonction "execute" mise à jour si cela fonctionne. –

+0

wconenen - Oui, c'est une super fonctionnalité de python, vous pouvez facilement remplacer les interfaces par imitation. – monkut

2

Vous pouvez regarder dans pexpect (http://www.noah.org/wiki/Pexpect)

Il permet de résoudre 1) et 2) de la boîte, préfixant la sortie pourrait être un peu plus difficile.

+0

+1 pour faire la plupart du travail sans réinventer la roue. Je suppose que le préfixe peut être fait en remplaçant les nouvelles lignes dans la sortie de la fonction .before. –

+0

Je viens de l'essayer, mais il s'avère être UNIX seulement. Une erreur est levée quand il essaie de charger le module "ressource" standard qui n'est pas disponible sur Windows. –

+0

Bummer, je n'utilise que python sur les systèmes UNIX, donc je n'ai pas considéré la portabilité. – SvenAron

1

Une autre option:

def run_test(test_cmd): 
    with tempfile.TemporaryFile() as cmd_out: 
     proc = subprocess.Popen(test_cmd, stdout=cmd_out, stderr=cmd_out) 
     proc.wait() 
     cmd_out.seek(0) 
     output = "".join(cmd_out.readlines()) 
    return (proc.returncode, output) 

Cela entrelacer stdout et stderr comme vous le souhaitez, dans un vrai fichier qui est commodément ouvert pour vous.

1

Ceci n'est en aucun cas une réponse complète ou exhaustive, mais vous devriez peut-être regarder dans le module Fabric.

http://docs.fabfile.org/0.9.1/

Fait exécution parallèle des commandes de shell et la manipulation assez facile erreur.

Questions connexes