Python – lancer un programme local avec sh

Voici un petit truc pour gérer une situation à laquelle j’ai été confronté récemment.

Il existe en Python un petit module fort sympathique : sh. Comme son nom l’indique, il permet de gérer des commandes shell, leurs paramètres et leur sortie de façon vachement intuitive. Autant dire que Perl vient de perdre un de ses derniers avantages par rapport à Python 😛

Mais alors où est le problème ? Le problème, c’est que les commandes systèmes marchent bien, mais comment gérer les commandes locales, « ./a.out » par exemple (lancer le binaire a.out dans le dossier courant) ? C’est en fait plutôt facile !

 

pythonL’exemple de base

L’exemple de base du module sh c’est ls:

1
2
3
4
5
6
7
8
9
10
11
12
>>> from sh import ls

>>> print(ls("-l"))

>>>

total 32
-rw-rw-r-- 1 baron_a baron_a 1094 mai 16 16:06 LICENSE
-rw-rw-r-- 1 baron_a baron_a 1083 mai 16 16:06 LICENSE~
-rw-rw-r-- 1 baron_a baron_a 6744 mai 16 16:08 README.md
-rw-rw-r-- 1 baron_a baron_a 10945 mai 16 16:06 vtkTexturingHelper.cpp
-rw-rw-r-- 1 baron_a baron_a 3454 mai 16 16:06 vtkTexturingHelper.h

C’est le fonctionnement de base du module sh.

Le problème

Le problème se pose lorsqu’on veut exécuter un programme avec son nom absolu, potentiellement avec des « / » ou des « . » dedans (voire les deux, comme dans « ./a.out »):

1
2
3
4
5
>>> from sh import ./a.out
File "<stdin>", line 1
from sh import ./a.out
^
SyntaxError: invalid syntax

La syntaxe des imports en Python nous interdit de placer ce genre de caractères ici.

« Pas de problèmes, que des solutions »

Il existe pourtant un moyen de s’en sortir dans ces situations : il faut alors délaisser la syntaxe un peu « magique » des imports de sh et mettre un peu plus « les mains dans le cambouis ».

Le module sh possède un objet représentant les commandes: il s’agit de sh.Command. Dans un cas comme « ./a.out », nous n’avons d’autre choix que de passer par là:

1
2
3
import sh

prog = sh.Command("./a.out")

Pour, si nécessaire, lui passer des arguments, ce n’est, une fois de plus, pas aussi intuitif qu’avec les commandes systèmes, il faut se servir de la méthode bake :

1
prog = prog.bake("Kirk", "Spock") # Renvoie la commande ./a.out prenant en paramètres Kirk et Spock

Il suffit ensuite d’appeler prog() comme n’importe quelle autre fonction, ce qui retourne alors un objet sh.RunningCommand duquel on peut extraire la sortie standard, sortie d’erreur, etc.

J’en profite pour remettre le code au complet:

1
2
3
4
5
6
7
import sh

prog = sh.Command(« ./a.out »)
prog = prog.bake("Kirk", "Spock")
output = prog()

print "Sortie standard:", output.stdout, "Sortie erreur:", output.stderr

Bon, c’était un article très rapide, mais c’est en partie car la documentation du module est quand même très complète, du coup, même les non-initiés ne devraient avoir aucun problème à voir comment ça marche.

La rédaction de cet article date d’ailleurs d’une époque où ce comportement n’était pas bien documenté. Maintenant, il y a un joli encart dans la doc exprès pour ce genre de cas, ce qui rend un peu cet article inutile, mais bon, c’était écrit… 🙂

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.