2010-09-27 16 views
2

J'ai joué avec le module subprocess pour envoyer itérativement chaque ligne d'un fichier d'entrée à un processus créé par la commande suivante.Transmission de données à distance ligne par ligne en Python

ssh -t -A $host 'remote_command' 

Le remote_command attend une ligne dans son STDIN, exécute une partie sur la ligne et itère le cycle jusqu'à STDIN ferme ou atteint EOF.

Pour ce faire, ce que je faisais était:

process = subprocess.Popen("ssh -t -A $host 'remote_command'", 
          shell=True, 
          stdin=subprocess.PIPE) 
for line in file('/tmp/foo'): 
    process.stdin.write(line) 
    process.stdin.flush() 
process.stdin.close() 

Mais ce que j'ai découvert que la méthode ci-dessus n'est pas assez robuste, car il est souvent que remote_command se termine prématurément sans traitement le contenu entier (bien que parfois le même code réussisse sans problème).

La situation est la même quand j'emploie une autre, quoique très simiar approche:

process = subprocess.Popen("ssh -t -A $host 'remote_command'", 
          shell=True, 
          stdin=file('/tmp/foo')) 

La question est: Comment puis-je faire en sorte que chaque ligne dans un fichier d'entrée être envoyés, reçus, et traité jusqu'à la fin par la machine distante en Python?

Répondre

2

Si cette ...

process = subprocess.Popen("ssh -t -A $host 'remote_command'", 
          shell=True, 
          stdin=subprocess.PIPE) 
for line in file('/tmp/foo'): 
    process.stdin.write(line) 
    process.stdin.flush() 
process.stdin.close() 

... est l'ensemble de votre programme, il ne fonctionnera pas (nécessairement).

Bien que le dernier process.stdin.close() veillera à ce que toutes les données ont été envoyées au processus ssh avant que votre programme se termine, il ne garantit pas que le processus ssh a envoyé toutes les données à travers le réseau, il peut bien être quelques données en suspens pour l'envoyer.

Malheureusement, étant donné que le processus ssh est un processus enfant de votre programme, puis lorsque votre programme se termine, le processus ssh recevra un SIGHUP qui va tuer immédiatement, potentiellement avant qu'elle ne se termine l'envoi de toutes ses données.

Tant que le remote_command se termine quand il frappe EOF, ce n'est pas un problème, et vous pouvez soit demander le processus ssh d'ignorer la SIGHUP, et continuer à fonctionner en arrière-plan ...

process = subprocess.Popen("nohup ssh -t -A $host 'remote_command'", ...) 

... ou demandez à votre programme d'attendre que le processus ssh pour terminer, en ajoutant ...

process.wait() 

... à la fin de votre programme.


Mise à jour

Un examen plus poussé, il ressemble à un processus obtient seulement SIGHUP si son contrôle se termine TTY, pas son processus parent.

Cela peut être dû à l'option -t qui crée un nouveau tty de contrôle sur l'hôte distant, et qui se termine avant la fin du sous-processus généré par spawns.

Dans ce cas, vous pourriez avoir besoin ...

process = subprocess.Popen("ssh -t -A $host 'nohup remote_command'", ...) 

... ou essayer sans l'option -t.

+0

Merci encore pour l'idée que je n'ai pas vue ailleurs. J'essaierai 'nohup' et je reviendrai vers vous. – OTZ

+0

@OTZ Voir la mise à jour de la réponse. Cela pourrait aider à expliquer ce qu'est 'remote_command', donc je peux essayer de reproduire le problème. – Aya

0

Au lieu de contourner un sous-processus, il est préférable d'utiliser quelque chose comme paramiko. Mais dans les deux cas, si votre connexion se termine avant que vous ayez envoyé toutes vos données, vous pouvez attraper cette exception et vous saurez que vous devez réessayer. Dans le cas où le processus meurt prématurément, vous devriez être capable de lire le code de sortie du processus.

+0

Quels seraient les avantages d'utiliser paramiko plutôt qu'un sous-processus dans ce cas? – OTZ

+0

L'avantage d'utiliser paramiko est que vous auriez plus de visibilité sur la raison pour laquelle la connexion a échoué car vous obtiendrez une exception du flux de communication plutôt qu'un simple code d'erreur 1 ou 0 du binaire ssh à travers le sous-processus. – synthesizerpatel

1

Vous ne pouvez pas faire beaucoup plus que ce que vous avez fait pour vous assurer que toutes les entrées envoyées à votre processus fils. Votre deuxième exemple est meilleur que le premier, à mon avis. Ce que vous pouvez faire est d'inspecter le code retour de votre processus fils.

return_code = p.wait() 

Votre commande à distance devrait revenir probablement 0 à la réussite et quelque chose non nul si une erreur est survenue.

0

Je dirais que votre meilleur pari serait d'utiliser le tube de réponse pour capturer les résultats de la commande à distance et s'assurer que vous atteignez une invite entre les lignes et après chaque ligne. BTW J'ai parfois trouvé qu'une commande fictive comme ls -l à la fin d'une session de liaison à distance aide à s'assurer que le traitement est terminé avant de laisser tomber la connexion.