Pages : 1
#1 Le 25/08/2009, à 22:36
- Totor
[Résolu] Python et les pipes
Bonsoir,
Comme indiqué dans un précédant post je m'initie à Python.
Pour ce faire, j'ai un "petit" projet personnel dont voici l'une des fonctionnalités (la plus capitale à vrai dire) :
Depuis python, exécuter unitairement et « contrôler » des instructions bash (le contrôle se limitant à la récupération de la valeur d'une variable)
En gros, j'aimerai avoir un processus bash en parallèle de mon processus Python et lui envoyer des instructions entre 2 lignes de code Python. L'environnement bash doit être conservé entre 2 lignes Python.
Pour cela j'utilise des pipes afin de communiquer entre les 2 environnements.
L'env. Bash est constitué d'un petit script « serveur » à l'écoute des demandes de l'environnement Python.
Voici le script (pipe.sh) :
#!/bin/bash
# set -x
py_fichierLog="/tmp/pipe.$$.log"
py_fichierErr="/tmp/pipe.$$.err"
py_fichierTrc="/tmp/pipe.$$.trc"
exec 1> >(tee -a "${py_fichierTrc}" )
exec 2> >(tee -a "${py_fichierErr}" >&2)
exec 3>>"${py_fichierLog}"
py_pipeIN="$1"
py_pipeOUT="$2"
cat <<EOF >&3
$(date)
Nom du pipe entrant: ${py_pipeIN}
Nom du pipe sortant: ${py_pipeOUT}
EOF
[ ! -p "${py_pipeIN}" ] && mkfifo "${py_pipeIN}"
[ ! -p "${py_pipeOUT}" ] && mkfifo "${py_pipeOUT}"
py_fin=1
while [ ${py_fin} -eq 1 ]
do
read py_cmd < "${py_pipeIN}"
echo "Recieved command : ${py_cmd}" >&3
py_cmd="${py_cmd:-END}"
[ "${py_cmd}" = "END" ]
py_fin=$?
if [ ${py_fin} -eq 1 ] ; then
if [[ "${py_cmd}" =~ ^GET:.+$ ]]; then
py_variable="${py_cmd#GET:}"
echo "Output value of ${py_variable} ---> ${!py_variable}" >&3
echo ${!py_variable} >> "${py_pipeOUT}"
elif [[ "${py_cmd}" =~ ^GETA:.+$ ]]; then
py_variable="${py_cmd#GETA:}"
echo "Output value of ${py_variable} ---> $(eval echo "\${${py_variable}[@]}")" >&3
eval echo "\${${py_variable}[@]}" > "${py_pipeOUT}"
else
echo -n "Evaluating '${py_cmd}' - return status : " >&3
eval "${py_cmd}"
echo "$?">&3
fi
fi
done
[ -p "${py_pipeIN}" ] && rm -f "${py_pipeIN}"
[ -p "${py_pipeOUT}" ] && rm -f "${py_pipeOUT}"
echo "End." >&3
exit
Le script Python :
#!/usr/bin/python3.0
import subprocess
import os
import sys
nomFichier='/tmp/' + sys.argv[0] .rstrip("py") + str(os.getpid() )
nomPipeOUT=nomFichier + '.out'
nomPipeIN=nomFichier +'.in'
# lancement process bash
p=subprocess.Popen( "./pipe.sh " + nomPipeOUT + ' ' + nomPipeIN, shell=True)
# Attente que le process bash ait créé les 2 pipes
while not os.path.exists(nomPipeOUT) or not os.path.exists(nomPipeIN):
None
# Ouverture de pipe en écriture
pipeOUT=os.open(nomPipeOUT, os.O_WRONLY)
# envoi des données
os.write(pipeOUT,'((TOTO++));\n'.encode())
os.write(pipeOUT,'((TOTO++));\n'.encode())
os.write(pipeOUT,'((TOTO++));\n'.encode())
os.write(pipeOUT,'TABLEAU=( a b c );\n'.encode())
os.write(pipeOUT,'echo "(bash)TOTO=${TOTO}"\n'.encode())
# on demande la valeur de la variable TOTO
os.write(pipeOUT,'GET:TOTO\n'.encode())
# Ouverture du pipe en lecture
pipeIN=os.open(nomPipeIN, os.O_RDONLY)
# récupération et affichage de la valeur
print('(Python) TOTO=' + os.read(pipeIN, 256).decode())
#os.close(pipeIN)
os.write(pipeOUT,'GETA:TABLEAU\n'.encode())
#pipeIN=os.open(nomPipeIN, os.O_RDONLY)
print('(Python) TABLEAU=' + os.read(pipeIN, 256).decode())
# fin du process bash
os.write(pipeOUT,'END\n'.encode())
print("FIN du test")
Mon problème étant celui-ci :
Il arrive parfois qu'il y ait un « Broken pipe » :
$./test.py
(bash)TOTO=3
(Python) TOTO=3
FIN du testTraceback (most recent call last):
File "./test.py", line 35, in <module>
os.write(pipeOUT,'END\n'.encode())
OSError: [Errno 32] Broken pipe
La question est : pourquoi ? Un processus s'exécute plus rapidement que l'autre ?
La version de python utilisée :
$python --version
Python 3.0.1+
Autre chose :
Je n'ai pas réussi à créer les pipes nommés depuis Python. Lorsque je procédais ainsi, je n'arrivais qu'à obtenir qu'une seule instruction dans mon environnement bash. Dès que je la lisais, le fichier « pipe » était supprimait...
j'ai essayé avec les os.pipe(), os.open(), open(), os.Popen() en précisant les stdin, stdout... mais c'est la seule façon que j'ai trouvé pour arriver à mon but.
(c'est ce qui justifie la boucle d'attente de la création des 2 pipes par l'environnement bash)
donc, toutes les remarques (solutions) sur le principe sont bonnes à prendre pour moi
L'objectif de ce script python est un test de faisabilité, la gestion des erreurs est donc volontairement absente.
Merci !
EDIT :
- Si j'effectue 2 fois une demande de valeur, j'obtiens en permanance l'erreur "OSError: [Errno 32] Broken pipe"
Je dois fermer le pipe de lecture via Python puis le ré-ouvrir pour que cela fonctionne de temps à autre ...
c.a.d les lignes os.close(pipeIN) et pipeIN=os.open(nomPipeIN, os.O_RDONLY) (qui sont actuellement en commentaires)
une idée ?
- j'ai modifié les sources, donc les n° de lignes du "Traceback" fourni ci-dessus ne sont lus valables mais il reste d'actualité.
Dernière modification par Totor (Le 26/08/2009, à 22:45)
-- Lucid Lynx --
Hors ligne
#2 Le 26/08/2009, à 01:48
- ADcomp
Re : [Résolu] Python et les pipes
Autre chose :
Je n'ai pas réussi à créer les pipes nommés depuis Python. Lorsque je procédais ainsi, je n'arrivais qu'à obtenir qu'une seule instruction dans mon environnement bash. Dès que je la lisais, le fichier « pipe » était supprimait...
j'ai essayé avec les os.pipe(), os.open(), open(), os.Popen() en précisant les stdin, stdout... mais c'est la seule façon que j'ai trouvé pour arriver à mon but.
(c'est ce qui justifie la boucle d'attente de la création des 2 pipes par l'environnement bash).
Utilise Popen du module "subprocess" .. http://docs.python.org/library/subprocess.html
his module intends to replace several other, older modules and functions, such as:
os.system
os.spawn*
os.popen*
popen2.*
commands.*
David [aka] ADcomp
Hors ligne
#3 Le 26/08/2009, à 10:02
- Totor
Re : [Résolu] Python et les pipes
Bonjour,
Oui, c'est la 1ère chose que j'ai fait. Mais je n'ai pas réussi. C'est d'ailleurs ce que j'ai mentionné mais avec une erreur (désolé) :
[...], os.Popen() en précisant les stdin, stdout... mais c'est la seule façon que j'ai trouvé pour arriver à mon but.
C'est subprocess.Popen et non os.Popen ...
Je vais tout de même m'y repencher...
En attendant, d'autres pistes ?
-- Lucid Lynx --
Hors ligne
#4 Le 26/08/2009, à 13:13
- Totor
Re : [Résolu] Python et les pipes
Bon voilà, le résultat de mes tests avec subprocess.Popen :
Le script Python :
#!/usr/bin/python3.0
import subprocess
import os
import sys
nomFichier='/tmp/' + sys.argv[0] .rstrip("py") + str(os.getpid() )
# lancement process bash
p=subprocess.Popen( "./pypipe.sh", stdin=subprocess.PIPE, shell=True)
pOut=p.communicate('TOTO=1\n'.encode())[0]
pOUT, pERR = p.communicate('GET:TOTO\n'.encode())
print('(Python) TOTO=' + pOUT)
# fin du process bash
p.communicate('END\n'.encode())
print("FIN du test")
le script bash :
#!/bin/bash
set -x
py_fin=1
while [ ${py_fin} -eq 1 ]
do
read py_cmd
#py_cmd="${py_cmd:-END}"
[ "${py_cmd}" = "END" ]
py_fin=$?
if [ ${py_fin} -eq 1 ] && [ "${py_cmd}" ]; then
if [[ "${py_cmd}" =~ ^GET:.+$ ]]; then
py_variable="${py_cmd#GET:}"
eval echo "\${${py_variable}[@]}"
else
eval "${py_cmd}"
fi
fi
done
exit
Mon constat :
Si je précise stdin en paramètre de Popen, le script python bloque à la ligne pOut=p.communicate('TOTO=1\n'.encode())[0]
Pour ce qui est du script bash, il boucle comme si ce qu'il lisait dans son stdin est vide
Si je ne précise pas stdin, c'est le script shell qui bloque car il attend quelque chose dans stdin
qu'est-ce qui m'échape ?
Dernière modification par Totor (Le 26/08/2009, à 13:15)
-- Lucid Lynx --
Hors ligne
#5 Le 26/08/2009, à 22:45
- Totor
Re : [Résolu] Python et les pipes
Bonsoir,
J'ai trouvé une solution alternative qui me convient.
J'utilise désormais la package pexpect et je conserve le script bash.
(avec la seule doc "complète" que j'ai trouvé : http://pexpect.sourceforge.net/pexpect.html)
Cependant la compilation (lors de l'installation) de ce package pour python 3.0 n'aboutie pas. N'ayant pas les compétences nécessaires pour modifier le code, je suis donc passé à Python 2.6.
Pour les scripts, voilà ce que ça donne :
Python :
#!/usr/bin/python
import pexpect
def _py_status():
return "py_status="
def initBash():
child=child = pexpect.spawn('./pypipe.sh')
if child.getecho():
child.setecho(False)
return child
def sendCmd(childProcess, cmdToExecute):
childProcess.sendline(cmdToExecute)
flux=''
retour=''
if cmdToExecute != 'END':
while not retour.startswith(_py_status()):
retour=childProcess.readline()
if not retour.startswith(_py_status()):
if flux != '': fluc+='\n'
flux+=retour
print retour
else:
return [retour.lstrip(_py_status()), flux ]
else: return [ '0', '' ]
def getValue(childProcess, variableName):
childProcess.sendline('GET:'+variableName)
return childProcess.readline()
child = initBash()
print ('Status:' + sendCmd(child,'TOTO=1') [0])
print ('Status:' + sendCmd(child,'((TOTO++))') [0])
print ('Status:' + sendCmd(child,'echo "(Bash) TOTO=${TOTO}"') [0])
print('(Python)TOTO='+getValue(child,'TOTO'))
print ('Status:' + sendCmd(child,'TABLEAU=("1 2 3" 2 3)')[0])
print ('Status:' + sendCmd(child,'echo "(Bash) NB_ELE=${#TABLEAU[@]}"') [0])
print('(Python)TABLEAU='+getValue(child,'TABLEAU'))
sendCmd(child,'END')
child.wait()
Pour le bash :
#!/bin/bash
#set -x
py_fichierLog="/tmp/$(basename "$0" .sh).$$.log"
py_fichierErr="/tmp/$(basename "$0" .sh).$$.err"
py_fichierTrc="/tmp/$(basename "$0" .sh).$$.trc"
exec 1> >(tee -a "${py_fichierTrc}" )
exec 2> >(tee -a "${py_fichierErr}" >&2)
exec 3>"${py_fichierLog}"
echo "$(date) : Start listening ..." >&3
py_fin=1
while [ ${py_fin} -eq 1 ]
do
read py_cmd
echo "Recieved command : ${py_cmd}" >&3
#py_cmd="${py_cmd:-END}"
[ "${py_cmd}" = "END" ]
py_fin=$?
if [ ${py_fin} -eq 1 ] && [ "${py_cmd}" ]; then
if [[ "${py_cmd}" =~ ^GET:.+$ ]]; then
py_variable="${py_cmd#GET:}"
py_value="$(eval echo "\${${py_variable}[@]}")"
echo "Output value of ${py_variable} ---> ${py_value}" >&3
echo "${py_value}"
else
echo "Evaluating '${py_cmd}'..." >&3
eval "${py_cmd}"
py_status="$?"
echo "Return status : ${py_status}" >&3
echo "py_status=${py_status}"
fi
fi
done
echo "$(date) : Stop listening." >&3
exit
Note : si quelqu'un trouve une solution avec les pipes (même si je compte garder cette solution) ou comment compiler le packages pexpect en python 3.0, je suis preneur
Dernière modification par Totor (Le 28/08/2009, à 02:29)
-- Lucid Lynx --
Hors ligne
Pages : 1