Question
Ca fait un bon moment que je suis coincé sur l’exécution de tee sur le serveur. Je sais qu’il y a les bonnes données dans le pipe stdin. J’ai une boucle qui l’affiche mais lorsque que je fais:
p = subprocess.Popen(bash,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
out, err = p.communicate()#wait for tee to be done
Il me crée le fichier, mais il est toujours vide, comme si son stdin était vide. Je ne comprends absolument pas ce comportement, tout le reste semble fonctioner dans mes affaires.
Réponse
Ici, l’auteur de la question présente une approche d’utilisation d’une
instance de subprocess.Popen
différente de celle proposée dans l’énoncé
du TP1. Je présume que la variable bash
à la première ligne du bout de code inclus est une variable dûment définie à
quelque chose comme la chaîne "/bin/bash"
; le reste de la séquence d’appel
du constructeur correspond à ce qui est présenté dans l’énoncé.
La différence réside dans l’usage de la méthode p.communicate()
; dans
l’énoncé, on utilisait plutôt directement les pipes p.stdin
et p.stdout
,
ainsi que la méthode p.wait()
. La méthode p.communicate()
offre une sorte
de raccourci pour échanger des données avec un coprocessus (i.e. un processus
dont on fournit l’entrée et recueille la sortie). Quand on invoque
out, err = p.communicate(input)
Le programme reçoit sur son entrée standard les octets de la chaîne input
–
et c’est tout. L’appel attend ensuite que le programme démarré par le
constructeur se termine, recueillant dans des chaînes en mémoire les
octets écrits par le programme sur sa sortie standard et sur son erreur
standard, et retourne le tout respectivement dans out
et err
. Ainsi, la
méthode p.communicate()
est idéale lorsqu’on échange des données avec un
coprocessus non interactif: on donne des données en entrée une seule fois,
on recueille les données en sortie une seule fois, puis le coprocessus est
terminé et c’est tout!
Ce n’est pas du tout le genre de communication qu’on désire effectuer dans le
TP1 entre le serveur et le programme interactif qu’il démarre. Nous
désirons être en mesure d’effectuer un nombre arbitraire d’échanges
d’informations. De plus, on ne veut pas recueillir les sorties du programme
une seule fois, lorsqu’il se termine: on veut les recueillir au fur et à
mesure que le programme les génère. Donc, je ne crois pas que
p.communicate()
soit l’approche à préconiser.
Pour revenir au comportement observé par l’auteur de la question, il est
parfaitement en accord avec ma description du comportement de
p.communicate()
. Dans l’invocation ci-haut, en ne passant pas de paramètre,
le pipe en entrée du shell coprocessus est immédiatement fermé; le shell
ne reçoit donc aucune entrée. Lorsque le shell détecte la fin du fichier en
entrée, il termine son exécution. C’est pourquoi, tant dans out
que dans
err
, l’auteur de la question reçoit des chaînes vides.
Finalement, je ne suis pas certain, mais en relisant le texte original de la
question, j’ai l’impression que l’auteur semble vouloir implanter son
programme ltee
et exécutant le programme tee
… Ce serait une erreur. La
similarité entre tee
et ltee
est conceptuelle, pas mécanique. Le programme
tee
sert à sauvegarder la sortie standard d’un programme dans un fichier sur
la machine où le programme est exécuté, en plus d’en faire écho sur la sortie
standard. Considérons que j’exécute la commande date
de la manière suivante:
$ date | tee allo
Thu May 15 21:32:42 EDT 2014
Cette sortie de la commande date
aurait aussi été sauvegardée dans le fichier
allo
dans le répertoire courant. Maintenant, dans un contexte où j’exécute
cette même commande date
sur un serveur distant via resh
/reshd
, je peux
vouloir sauvegarder la sortie de la commande sur ma propre machine, plutôt
que sur le serveur. J’utiliserais alors la commande ltee
. Simulons – à vous
de deviner qu’est-ce qui est une commande et qu’est-ce qui est une sortie,
ainsi que qu’est-ce qui est exécuté localement vs. qu’est-ce qui est exécuté
sur le serveur distant:
$ ls
resh
$ ./resh serveur.lointain.com 9887
ls
ltee lcat reshd resh
date
Thu May 15 21:35:21 EDT 2014
id | tee allo
Thu May 15 21:35:33 EDT 2014
ls
allo ltee lcat reshd resh
cat allo
Thu May 15 21:35:33 EDT 2014
date | ./ltee chezmoi
Thu May 15 21:42:10 EDT 2014
ls
allo ltee lcat reshd resh
exit
$ ls
chezmoi resh
$ cat chezmoi
Thu May 15 21:42:10 EDT 2014
Le lien entre la commande standard cat
et la commande lcat
que vous devrez
programmer est identique: ces deux commandes sont conceptuellement analogues,
mais leurs implantations respectives n’ont rien à voir. La commande lcat
ne
sera pas basée sur la commande cat
; cette commande n’a pas à être exécutée
par lcat
.