Comment exécuter des scripts bash avec Python ?
Si vous êtes un utilisateur de Linux, il y a de fortes chances que vous appréciez les commandes shell.
Et si, en parallèle, vous utilisez Python, vous avez certainement déjà envisagé d'automatiser certaines tâches. C'est une méthode efficace pour gagner du temps. De la même manière, il est courant d'utiliser des scripts bash pour des automatisations.
Cependant, Python s'avère souvent plus commode que bash pour l'écriture de scripts. De plus, la gestion de scripts Python est généralement plus simple comparée à celle des scripts bash, qui peuvent devenir difficiles à maintenir une fois leur complexité augmentée.
Mais que faire si vous possédez déjà des scripts bash que vous aimeriez intégrer à vos processus Python ?
Existe-t-il une solution pour lancer des commandes et des scripts bash à partir de Python ?
Heureusement, oui. Python intègre un module appelé "subprocess" spécialement conçu pour l'exécution de commandes et de scripts dans un environnement Python. Découvrons ensemble comment concrètement utiliser ce module pour exécuter vos commandes et scripts bash.
Exécuter des commandes bash
Comme vous l'avez peut-être déjà deviné, le module "subprocess" est l'outil idéal pour lancer des commandes et des scripts bash. Il propose diverses méthodes et classes dédiées à cet usage.
Au sein de ce module, deux éléments principaux sont à retenir : la méthode run() et la classe Popen. Ces deux outils nous permettent d'intégrer l'exécution de commandes bash dans nos scripts Python. Examinons-les plus en détail.
subprocess.run()
La méthode subprocess.run() attend une liste de chaînes de caractères comme argument positionnel. Cet argument est obligatoire, car il contient la commande bash ainsi que ses éventuels arguments. Le premier élément de la liste correspond au nom de la commande, et les éléments suivants sont les arguments de cette commande.
Voici un exemple simple :
import subprocess subprocess.run(["ls"])
Le script ci-dessus affichera le contenu du répertoire courant. Ici, la commande ls est lancée sans argument supplémentaire. Il est tout à fait possible de fournir des arguments tels que -l, -a ou -la à la commande ls.
Voyons un autre exemple avec des arguments :
import subprocess subprocess.run(["ls", "-la"])
Cette commande affichera tous les fichiers, y compris les fichiers cachés, ainsi que les informations détaillées sur les permissions. L'argument -la permet donc de visualiser des informations complémentaires sur les fichiers et répertoires, y compris les fichiers cachés.
Il est fréquent de commettre des erreurs lors de la saisie de commandes. Afin de pouvoir capturer ces erreurs et les traiter ultérieurement, nous pouvons utiliser l'argument nommé stderr.
Voici un exemple :
import subprocess result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True) print(result.stderr)
Assurez-vous que le fichier "sample.txt" n'existe pas dans votre répertoire courant pour observer l'erreur. L'argument stderr défini à PIPE permet de rediriger l'erreur vers un objet, auquel on pourra accéder via l'attribut stderr. L'argument text=True indique que la sortie doit être une chaîne de caractères.
De manière similaire, il est possible de capturer la sortie standard de la commande en utilisant l'argument stdout.
import subprocess result = subprocess.run(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) print(result.stdout)
subprocess.run() – Entrée
Il est également possible de fournir une entrée à la commande en utilisant l'argument nommé input. Les entrées sont fournies sous forme de chaînes de caractères. L'argument text doit donc être défini à True, car par défaut, l'entrée est attendue sous forme d'octets.
Prenons un exemple :
import subprocess subprocess.run(["python3", "add.py"], text=True, input="2 3")
Dans cet exemple, le script Python add.py reçoit deux nombres en entrée grâce à l'argument input.
subprocess.Popen()
La classe subprocess.Popen() offre un niveau de contrôle plus fin que la méthode subprocess.run(). Elle permet, entre autres, de suivre l'état d'exécution d'une commande, d'obtenir sa sortie ou de lui fournir une entrée. L'instance de la classe Popen fournit les outils nécessaires pour ces actions.
Plusieurs méthodes de la classe subprocess.Popen() méritent une attention particulière. Examinons-les une par une avec des exemples concrets.
wait()
La méthode wait() est utilisée pour suspendre l'exécution du script Python jusqu'à ce que la commande soit terminée. Les lignes de code qui suivent l'appel à wait() ne seront exécutées qu'après la fin de la commande. Voici un exemple :
import subprocess
process = subprocess.Popen(["ls", "-la"])
print("Terminé!")
Si vous exécutez ce code, vous constaterez que le message "Terminé !" s'affiche avant que la commande ls ne soit exécutée. Pour éviter ce comportement, utilisez la méthode wait(). Attendez la fin de l'exécution de la commande :
import subprocess
process = subprocess.Popen(["ls", "-la"])
process.wait()
print("Terminé!")
L'exécution de ce code démontre que la méthode wait() fonctionne comme prévu : le message s'affiche uniquement après la fin de la commande.
communicate()
La méthode communicate() permet de récupérer la sortie standard, l'erreur standard et d'envoyer une entrée à la commande. Elle renvoie un tuple contenant respectivement la sortie et l'erreur. Voici un exemple :
import subprocess process = subprocess.Popen(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) result = process.communicate() print(result)
subprocess.Popen() – Entrée
Il n'est pas possible de transmettre directement une entrée à l'instance de la classe Popen. L'argument nommé stdin doit être utilisé pour fournir l'entrée. L'instance de Popen fournit un objet stdin, avec la méthode write pour envoyer l'entrée à la commande.
Comme précédemment, l'entrée est par défaut attendue sous forme d'octets. Il est donc impératif de définir l'argument text=True lors de la création de l'instance de Popen.
Prenons un exemple :
import subprocess
process = subprocess.Popen(["python3", "add.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
process.stdin.write("2 3")
process.stdin.close()
print(process.stdout.read())
poll()
La méthode poll() permet de vérifier si l'exécution de la commande est terminée. Elle renvoie None si la commande est toujours en cours d'exécution. Voici un exemple :
import subprocess
process = subprocess.Popen(['ping', '-c 5', 'geekflare.com'], stdout=subprocess.PIPE, text=True)
while True:
output = process.stdout.readline()
if output:
print(output.strip())
result = process.poll()
if result is not None:
break
Dans cet exemple, la commande ping est exécutée avec 5 requêtes. Une boucle infinie itère jusqu'à ce que la commande soit terminée. La méthode poll() vérifie l'état d'exécution de la commande. Lorsque poll() renvoie une valeur autre que None, l'exécution est terminée et la boucle s'arrête.
Exécuter des scripts bash
Nous avons vu comment exécuter des commandes. Voyons maintenant comment exécuter des scripts bash depuis Python.
Le module "subprocess" dispose d'une méthode appelée call. Cette méthode est utilisée pour exécuter des scripts bash et renvoie le code de sortie du script. Le code de sortie par défaut pour un script bash est 0. Prenons un exemple.
Créez un script bash nommé practice.sh avec le contenu suivant :
#!/bin/bash echo "Hello, World!" exit 1
Maintenant, écrivez un script Python pour exécuter le script bash ci-dessus :
import subprocess
exit_code = subprocess.call('./practice.sh')
print(exit_code)
Voici le résultat que vous obtiendrez en exécutant le script Python :
Hello, World! 1
Conclusion
Nous avons exploré différentes méthodes pour exécuter des commandes bash et des scripts depuis Python. Vous pouvez désormais les utiliser pour automatiser efficacement vos tâches.
Bon codage 👨💻