Użytkownicy systemu Linux z pewnością docenią potęgę poleceń powłoki.
Osoby pracujące z językiem Python często poszukują sposobów na automatyzację zadań, co pozwala zaoszczędzić cenny czas. Czasem do tego celu wykorzystywane są skrypty bash.
Jednak Python oferuje bardziej efektywne narzędzia do tworzenia skryptów niż bash. Ponadto, zarządzanie skryptami w Pythonie jest znacznie prostsze, szczególnie gdy projekty rozrastają się. Utrzymanie dużych skryptów bash może stać się skomplikowane.
Co jednak zrobić, gdy posiadamy już gotowe skrypty bash, które chcemy wywoływać z poziomu Pythona?
Czy istnieje metoda na uruchamianie poleceń i skryptów bash w programach napisanych w Pythonie?
Oczywiście! Python dysponuje wbudowanym modułem o nazwie `subprocess`, który umożliwia wykonywanie poleceń i skryptów bezpośrednio w skryptach Pythona. Przyjrzyjmy się bliżej, jak to działa.
Realizacja poleceń Bash
Jak już wspomniano, moduł `subprocess` jest kluczowy do uruchamiania poleceń i skryptów bash. Oferuje on różnorodne metody i klasy, które to umożliwiają.
W kontekście modułu `subprocess` warto zwrócić uwagę na jedną metodę i jedną klasę: `run` oraz `Popen`. To właśnie one umożliwiają nam wywoływanie poleceń bash z poziomu skryptów Pythona. Przeanalizujmy je po kolei.
`subprocess.run()`
Metoda `subprocess.run()` przyjmuje listę ciągów znaków jako argument pozycyjny. Lista ta zawiera polecenie bash wraz z jego ewentualnymi argumentami. Pierwszy element listy to nazwa polecenia, a pozostałe to argumenty.
Spójrzmy na prosty przykład:
import subprocess subprocess.run(["ls"])
Powyższy skrypt wyświetli zawartość bieżącego katalogu roboczego, co jest zgodne z działaniem polecenia `ls`. W tym przypadku nie podaliśmy żadnych argumentów, jedynie samo polecenie. Możemy jednak rozszerzyć to o argumenty polecenia `ls`, takie jak `-l`, `-a`, `-la` itp.
Oto przykład z argumentami:
import subprocess subprocess.run(["ls", "-la"])
Powyższe polecenie wyświetli wszystkie pliki i katalogi, w tym ukryte, wraz z dodatkowymi informacjami o uprawnieniach. Argument `-la` rozszerza działanie polecenia `ls` o wyświetlanie plików ukrytych i szczegółów.
Podczas pisania poleceń mogą pojawić się błędy. Domyślnie, błędy te spowodują przerwanie działania programu. Jak jednak je przechwycić i wykorzystać? Możemy to zrobić za pomocą argumentu słowa kluczowego `stderr`.
Spójrzmy na ten przykład:
import subprocess result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True) print(result.stderr)
Upewnij się, że w katalogu roboczym nie istnieje plik o nazwie `sample.txt`. Wartość argumentu `stderr` ustawiona na `PIPE` sprawia, że błąd zostanie zwrócony w obiekcie. Możemy uzyskać do niego dostęp za pomocą atrybutu `stderr`. Argument tekstowy `text=True` zapewnia, że wynik będzie ciągiem znaków.
Podobnie, możemy przechwycić standardowe wyjście polecenia, wykorzystując argument słowa kluczowego `stdout`:
import subprocess result = subprocess.run(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) print(result.stdout)
`subprocess.run()` – przekazywanie wejścia
Możliwe jest przekazywanie danych wejściowych do poleceń za pomocą argumentu słowa kluczowego `input`. Dane te przekazywane są w formacie ciągu znaków, dlatego ważne jest, aby ustawić `text=True`. Domyślnie dane wejściowe są traktowane jako bajty.
Oto przykład:
import subprocess subprocess.run(["python3", "add.py"], text=True, input="2 3")
W tym przypadku, skrypt Pythona `add.py` oczekuje dwóch liczb jako danych wejściowych. Przekazaliśmy je za pomocą argumentu `input`.
`subprocess.Popen()`
Klasa `subprocess.Popen()` oferuje bardziej zaawansowane możliwości niż metoda `subprocess.run()`. Umożliwia większą kontrolę nad procesem, na przykład sprawdzenie stanu wykonania polecenia, uzyskanie jego wyjścia czy podawanie wejścia.
Klasa `subprocess.Popen()` posiada kilka metod, które warto poznać. Przeanalizujmy je krok po kroku, na przykładach:
`wait()`
Metoda `wait()` służy do zatrzymania wykonania skryptu do czasu zakończenia polecenia. Kolejne linie kodu nie zostaną wykonane, dopóki polecenie nie zakończy swojej pracy. Spójrzmy na przykład:
import subprocess process = subprocess.Popen(["ls", "-la"]) print("Completed!")
Jeśli uruchomimy powyższy kod, zobaczymy, że komunikat „Completed!” zostanie wypisany przed wyświetleniem wyniku polecenia `ls`. Możemy uniknąć tej sytuacji, korzystając z metody `wait()`:
import subprocess process = subprocess.Popen(["ls", "-la"]) process.wait() print("Completed!")
Teraz, instrukcja `print` zostanie wykonana dopiero po zakończeniu wykonywania polecenia `ls`.
`communicate()`
Metoda `communicate()` służy do uzyskiwania danych wyjściowych i błędów polecenia oraz do przekazywania mu danych wejściowych. Zwraca krotkę, zawierającą odpowiednio standardowe wyjście i standardowy strumień błędów. Spójrzmy na przykład:
import subprocess process = subprocess.Popen(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) result = process.communicate() print(result)
`subprocess.Popen()` – przekazywanie wejścia
W przypadku klasy `Popen`, nie możemy bezpośrednio przekazać danych wejściowych. Musimy użyć argumentu kluczowego `stdin`, który udostępnia obiekt `stdin`. Ten obiekt posiada metodę `write`, służącą do wprowadzania danych.
Jak wspomniano wcześniej, domyślnie, dane wejściowe są traktowane jako obiekty bajtowe. Dlatego musimy pamiętać o ustawieniu `text=True` podczas tworzenia instancji `Popen`.
Spójrzmy na przykład:
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()`
Metoda `poll()` służy do sprawdzenia, czy polecenie zakończyło już swoje działanie. Jeśli polecenie wciąż jest wykonywane, metoda zwróci wartość `None`. Spójrzmy na przykład:
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
Powyższy kod wykorzystuje polecenie `ping` z pięcioma żądaniami. Pętla `while` iteruje do momentu, gdy polecenie zostanie zakończone. Metoda `poll` sprawdza status wykonania polecenia. Gdy metoda `poll` zwróci wartość inną niż `None`, pętla zostaje przerwana.
Wykonywanie skryptów Bash
Omówiliśmy już dwa sposoby wykonywania poleceń. Teraz przejdźmy do uruchamiania skryptów bash w skryptach Pythona.
Moduł `subprocess` udostępnia metodę o nazwie `call`, która służy do wykonywania skryptów bash. Metoda ta zwraca kod wyjścia ze skryptu bash. Domyślny kod wyjścia to `0`. Spójrzmy na przykład:
Utwórz skrypt bash o nazwie `practice.sh` z następującą zawartością:
#!/bin/bash echo "Hello, World!" exit 1
Teraz napisz skrypt Pythona, który uruchomi powyższy skrypt bash:
import subprocess exit_code = subprocess.call('./practice.sh') print(exit_code)
Po uruchomieniu skryptu Pythona, uzyskasz następujący rezultat:
Hello, World! 1
Podsumowanie
W artykule omówiliśmy, jak wykonywać polecenia i skrypty bash w języku Python. Dzięki tym metodom, proces automatyzacji zadań stanie się o wiele efektywniejszy.
Udanej pracy z kodem! 👨💻