<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>MGautier</title><link href="http://mgautier.fr" rel="alternate"></link><link href="http://mgautier.fr/feeds/python.atom.xml" rel="self"></link><id>http://mgautier.fr</id><updated>2010-02-22T17:55:00Z</updated><entry><title>Changer l'environnement bash avec python</title><link href="http://mgautier.fr/2010/02/astuce/change-env-bash-python.html" rel="alternate"></link><updated>2010-02-22T17:55:00Z</updated><author><name>Matthieu Gautier</name></author><id>tag:mgautier.fr,2010-02-22:/2010/02/astuce/change-env-bash-python.html/</id><summary type="html">&lt;p&gt;Il est possible de changer l'environnement bash en &amp;quot;sourçant&amp;quot; un script bash. Mais comment ça se passe si on veut
le faire avec un script python ?&lt;/p&gt;
&lt;div class="section" id="petit-rappel"&gt;
&lt;h2&gt;Petit rappel&lt;/h2&gt;
&lt;p&gt;Sous unix, chaque processus s'exécute dans un contexte particulier. Une des 'caractéristiques' de ce contexte est
l'environnement d'exécution représenté par les variables d'environnement. Chaque processus (excepté le premier, init)
est lancé par un autre processus (le processus père) et hérite d'une copie de l'environnement de ce dernier.
Comme le processus fils accède à une copie de l'environnement du processus père, il ne peut pas le modifier.
Ainsi, sous bash, si vous exécutez un programme (ou un script), à la sortie de ce script, l'environnement du
processus père (votre terminal ici) n'est pas modifié. Par contre, si vous interprétez le script dans le processus
courant à l'aide de l'instruction source, l'environnement du processus est modifié (notez bien qu'il n'y a pas de
processus père ou fils, car il n'y a qu'un seul processus)
Prenez ce script simple que nous appellerons essai.sh (oui, je sais, ce n'est pas très imaginatif mais bon, ce n'est
pas le but de ce billet) :&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;variable TRUC : $TRUC&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;modification de la variable d&amp;#39;environnement TRUC&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TRUC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;machin&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;variable TRUC après modification : $TRUC&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;Et dans un terminal, exécutez c'est quelques commandes :&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TRUC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bidule&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$TRUC&lt;/span&gt;
&lt;span class="go"&gt;bidule&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; ./essai.sh
&lt;span class="go"&gt;variable TRUC : bidule&lt;/span&gt;
&lt;span class="go"&gt;modification de la variable d&amp;#39;environnement TRUC&lt;/span&gt;
&lt;span class="go"&gt;variable TRUC après modification : machin&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$TRUC&lt;/span&gt;
&lt;span class="go"&gt;bidule&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;source &lt;/span&gt;essais.sh
&lt;span class="go"&gt;variable TRUC : bidule&lt;/span&gt;
&lt;span class="go"&gt;modification de la variable d&amp;#39;environnement TRUC&lt;/span&gt;
&lt;span class="go"&gt;variable TRUC après modification : machin&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$TRUC&lt;/span&gt;
&lt;span class="go"&gt;machin&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;On voit bien que la variable d'environnement TRUC n'as pas été modifiée dans l'environnement du processus père
lorsqu'on exécute le script, mais que, quand on le &amp;quot;source&amp;quot;, l'environnement est bien modifié. Tout ceci marche
bien quand on reste dans bash. Par contre, cela ne fonctionne pas avec python puisque que, bash n'interprétant
que des scripts bash et pas des scripts python, on ne peut les &amp;quot;sourcer&amp;quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="l-astuce"&gt;
&lt;h2&gt;L'astuce&lt;/h2&gt;
&lt;p&gt;Si on ne peut interpréter un script python avec bash il faut le faire avec python. Une fois le script interprété,
il nous reste plus qu'à traduire l'environnement python en bash. Comment ? En prenant toutes les variables
d'environnement et pour chacune d'entre elles, créer et exécuter une instruction bash correspondante.
Pour cela nous allons avoir :&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Un script python qui change l'environnement. C'est le script utilisateur qui est l'équivalent python de essai.sh&lt;/li&gt;
&lt;li&gt;Un script python (pylauncher.py) qui va lancer le script utilisateur et qui, en sortie, va donner les instructions bash correspondantes à l'environnement modifié par le script utilisateur.&lt;/li&gt;
&lt;li&gt;Un script bash (pysource.sh) qui va lancer pylauncher.py et évaluer les instructions bash.&lt;/li&gt;
&lt;li&gt;Un script bash (ou notre terminal) qui lance le tout et qui veut voir son environnement modifié.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le script utilisateur :
C'est le script python qui modifie l'environnement, c'est à vous de le créer en fonction de vos besoins. Ici nous
prendrons un script qui fait la même chose que essai.sh :&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;#-*- coding:utf-8 -*-&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;variable TRUC :&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TRUC&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;modification de la variable d&amp;#39;environnement TRUC&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TRUC&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;machin&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;variable TRUC après modification :&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TRUC&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;pylauncher.py&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;#-*- coding:utf-8 -*-&lt;/span&gt;

&lt;span class="c"&gt;# Pour éviter les conflits de nom, on sauvegarde l&amp;#39;espace de nom global avant qu&amp;#39;il ne soit &amp;quot;pollué&amp;quot; par nos variables.&lt;/span&gt;
&lt;span class="n"&gt;__globals__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c"&gt;# dump_env s&amp;#39;occupe de traduire l&amp;#39;environnement python en instructions bash.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dump_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dumpOutput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;dumpOutput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;export &lt;/span&gt;&lt;span class="si"&gt;%(key)s&lt;/span&gt;&lt;span class="s"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(value)s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="c"&gt;# On redéfinie    la sortie standard. Ainsi le script utilisateur fera ses &amp;quot;print&amp;quot; sur le descripteur de fichier 3 et on imprimera l&amp;#39;environnement sur le descripteur de fichier 1&lt;/span&gt;
    &lt;span class="n"&gt;dumpOutput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fdopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# On modifie quelques variables pour que le script utilisateur n&amp;#39;est pas conscience de pylauncher.py&lt;/span&gt;
    &lt;span class="n"&gt;__globals__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;__file__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="c"&gt;# On exécute le script utilisateur.&lt;/span&gt;
&lt;span class="c"&gt;# On redéfinie les espaces de nom globaux et locaux pour les mêmes raisons que le commentaire précédent.&lt;/span&gt;
&lt;span class="c"&gt;# On n&amp;#39;utilise pas os.system ou subprocess, car justement, on ne veut pas d&amp;#39;un processus fils. execfile est l&amp;#39;équivalent d&amp;#39;un source, mais à la mode python.&lt;/span&gt;
       &lt;span class="nb"&gt;execfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;__globals__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c"&gt;# On imprime l&amp;#39;environnement.&lt;/span&gt;
       &lt;span class="n"&gt;dump_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dumpOutput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pensez à rendre pylauncher.py exécutable puisqu'il sera lancé par sourcepy.sh&lt;/p&gt;
&lt;p&gt;Le script sourcepy.sh&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;./pylauncher.py $@&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# On initialise le descripteur de fichier 3.&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;3&amp;gt;&amp;amp;1

&lt;span class="c"&gt;# À ce moment on a trois descripteurs de fichier ouverts :&lt;/span&gt;
&lt;span class="c"&gt;# - le numéro 1 qui pointe vers la sortie standard. (là où sont imprimées les instructions bash)&lt;/span&gt;
&lt;span class="c"&gt;# - le numéro 2 qui pointe vers la sortie d&amp;#39;erreur.&lt;/span&gt;
&lt;span class="c"&gt;# - le numéro 3 qui pointe vers la sortie standard. (là où sont imprimées les sorties standard du script utilisateur)&lt;/span&gt;

&lt;span class="c"&gt;# On lance pylauncher.py qui va lancer le script utilisateur et on évalue les instructions bash pour retrouver l&amp;#39;environnement modifier par le script utilisateur&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;span class="c"&gt;# On ferme le descripteur de fichier 3.;;&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;3&amp;gt;&amp;amp;-
&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notez qu'il n'est pas nécessaire de créer un fichier sourcepy.sh, vous pouvez en faire une fonction que vous mettrez dans
votre .bashrc&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="on-lance-le-tout"&gt;
&lt;h2&gt;On lance le tout :&lt;/h2&gt;
&lt;p&gt;Dans un terminal :&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt;  ls
&lt;span class="go"&gt;pylauncher.py&lt;/span&gt;
&lt;span class="go"&gt;essai.py&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TRUC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bidule&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;source &lt;/span&gt;sourcepy.sh essai.py &lt;span class="c"&gt;# ou sourcepy essai.py si on a fait une fonction sourcepy&lt;/span&gt;
&lt;span class="go"&gt;variable TRUC : bidule&lt;/span&gt;
&lt;span class="go"&gt;modification de la variable d&amp;#39;environnement TRUC&lt;/span&gt;
&lt;span class="go"&gt;variable TRUC après modification : machin&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$TRUC&lt;/span&gt;
&lt;span class="go"&gt;machin&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;Et voilà !&lt;/p&gt;
&lt;/div&gt;
</summary><category term="bash"></category><category term="linux"></category><category term="python"></category></entry></feed>
