Jak z Pythona uruchomić skrypt bashowy i jak wykorzystać kod powrotu skryptu?

Czasami zachodzi potrzeba uruchomienia z naszego programu/skryptu pythonowego skryptu w Bashu. Oczywiście, można byłoby całą funkcjonalność tego skryptu przepisać w Pythonie, jednak czasami musimy użyć takiego rozwiązania i wtedy świetnie byłoby, gdyby można było odczytać kod powrotu skryptu bashowego.

W poniższym przykładzie mamy skrypt w Bashu, który sprawdza czy plik testfile istnieje w bieżącym katalogu. Jeśli istnieje, zwraca wartość 0, jeśli nie, zwraca 1. Dodatkowo dodałem kilka sleepów i skrypt wypisuje na ekranie ilość sekund do wywołania sprawdzenia.

sleep.sh:

#!/bin/bash
 
PLIK="./testfile"
 
echo "3"
sleep 1
echo "2"
sleep 1
echo "1"
sleep 1
if [ -e $PLIK ]; then
        exit 0
else
        exit 1
fi

W skrypcie pythonowym importujemy moduł subprocess, który umożliwia uruchamianie nowych procesów, podłączanie się do ich potoków wejścia, wyjścia i błędu oraz uzyskiwanie ich kodu powrotu.
Ze względów bezpieczeństwa zaleca się ustawienie parametru shell=False. Więcej informacji na ten temat można przeczytać tutaj.
Jeżeli potrzebujemy bardziej zaawansowanych opcji, warto użyć klasy Popen z modułu subprocess.

runbash.py:

#!/usr/bin/env python2
 
import subprocess
print('start')
if (subprocess.call('./sleep.sh', shell=False) == 0):
    print('plik istnieje')
else:
    print('plik nie istnieje')
print('koniec')

uruchomienie:

[bart@fedora20 python]$ ./runbash.py 
start
3
2
1
plik nie istnieje
koniec
[bart@fedora20 python]$ touch testfile
[bart@fedora20 python]$ ./runbash.py 
start
3
2
1
plik istnieje
koniec
[bart@fedora20 python]$

opis działania:

Przy pierwszym uruchomieniu w lokalnym katalogu nie było pliku testfile:

1. Skrypt runbash.py wywołał podproces sleep.sh
2. Instrukcja warunkowa if/else w sleep.sh sprawdziła, że plik nie istnieje i skrypt zakończył się z kodem powrotnym 1.
3. Instrukcja warunkowa if/else w runbash.py odczytała kod powrotny z sleep.sh (1) i wyświetliła na ekranie komunikat z informacją, że plik nie istnieje.

Przed drugim uruchomieniem utworzyliśmy plik testfile (touch testfile), sleep.sh zakończył się z kodem powrotu 0. Kod powrotu został odczytany przez runbash.py i skrypt wyświetlił informację, że plik istnieje.