Jestliže používáte operační systém Linux, jistě oceníte sílu příkazů v shellu.
A pokud jste programátor v jazyce Python, pravděpodobně jste se už pokoušeli automatizovat některé úlohy. To je efektivní způsob, jak ušetřit čas. Možná máte i nějaké skripty v bash pro automatizaci různých činností.
Python je pro psaní skriptů obecně vhodnější než bash. Správa skriptů v Pythonu je také jednodušší ve srovnání s bash skripty. Jakmile se bash skripty rozrostou, jejich údržba může být náročná.
Co když ale už máte hotové bash skripty, které potřebujete spouštět z Pythonu?
Existuje nějaký způsob, jak z Pythonu spouštět příkazy a skripty v bash?
Ano, Python disponuje vestavěným modulem s názvem `subprocess`, který se používá k provádění příkazů a skriptů přímo z Python skriptů. Pojďme se podívat, jak konkrétně spouštět bash příkazy a skripty v Pythonu.
Spouštění Bash Příkazů
Jak již bylo zmíněno, modul `subprocess` slouží k provádění příkazů a skriptů v bash. Nabízí různé metody a třídy pro tento účel.
V modulu `subprocess` byste měli znát zejména jednu metodu a jednu třídu. Jedná se o `run` a `Popen`. Tyto dvě nám umožňují provádět bash příkazy v Python skriptech. Probereme je postupně.
`subprocess.run()`
Metoda `subprocess.run()` přijímá jako poziční argument seznam řetězců. Tento seznam je povinný a obsahuje bash příkaz a případné argumenty pro něj. První položka v seznamu je název příkazu a zbývající položky jsou argumenty daného příkazu.
Podívejme se na krátký příklad.
import subprocess
subprocess.run(["ls"])
Výše uvedený skript vypíše obsah aktuálního pracovního adresáře, ve kterém se skript nachází. V tomto příkladu nejsou k příkazu `ls` zadány žádné argumenty, pouze samotný příkaz. Příkazu `ls` ale můžeme poskytnout další argumenty jako `-l`, `-a`, `-la` atd.
Zkusme to s argumenty příkazu:
import subprocess
subprocess.run(["ls", "-la"])
Tento příkaz zobrazí všechny soubory, včetně skrytých, spolu s informacemi o oprávněních. Argument `la` specifikuje, že chceme zobrazit podrobné informace o souborech a adresářích a také skryté soubory.
Může se stát, že při psaní příkazů dojde k chybám. Pokud chceme tyto chyby zachytit a dále s nimi pracovat, můžeme použít argument `stderr`.
Následuje příklad:
import subprocess
result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True)
print(result.stderr)
Ujistěte se, že v pracovním adresáři nemáte soubor s názvem `sample.txt`. Hodnota argumentu `stderr` je nastavena na `PIPE`, což umožňuje zachytit chybovou zprávu do objektu, ke kterému můžeme později přistupovat. Argument `text` zajistí, že výstup bude textový řetězec.
Podobně můžeme zachytit standardní výstup příkazu pomocí argumentu `stdout`.
import subprocess
result = subprocess.run(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(result.stdout)
`subprocess.run()` – Vstup
Vstup pro příkazy lze zadat pomocí argumentu `input`. Vstup musí být ve formě textového řetězce. Proto musíme nastavit argument `text` na `True`. Ve výchozím stavu by vstup byl chápán jako bajty.
Podívejme se na příklad:
import subprocess
subprocess.run(["python3", "add.py"], text=True, input="2 3")
V tomto příkladu Python skript `add.py` přijímá dvě čísla jako vstup. Vstup do tohoto skriptu jsme zadali pomocí argumentu `input`.
`subprocess.Popen()`
Třída `subprocess.Popen()` je pokročilejší než metoda `subprocess.run()`. Nabízí více možností pro provádění příkazů. Vytvoříme instanci `subprocess.Popen()` a můžeme ji použít pro různé účely, jako je zjištění stavu provádění příkazu, získání výstupu, zadávání vstupu atd.
Existuje několik metod třídy `subprocess.Popen()`, které je užitečné znát. Probereme je jednu po druhé, spolu s příklady kódu.
`wait()`
Metoda `wait()` se používá k pozastavení provádění skriptu Python, dokud se nedokončí provedení zadaného příkazu. Další řádky skriptu se nespustí, dokud se nedokončí předchozí příkaz. Podívejme se na příklad.
import subprocess
process = subprocess.Popen(["ls", "-la"])
print("Completed!")
Spusťte výše uvedený kód a sledujte výstup. Všimnete si, že zpráva „Completed!“ se vypíše před tím, než se příkaz `ls` provede. Tomuto chování se můžeme vyhnout použitím metody `wait()`, která počká, dokud se příkaz nedokončí.
import subprocess
process = subprocess.Popen(["ls", "-la"])
process.wait()
print("Completed!")
Pokud se podíváte na výstup tohoto kódu, uvidíte, že metoda `wait()` funguje podle očekávání. Příkaz `print` se provede až po dokončení příkazu `ls`.
`communicate()`
Metoda `communicate()` se používá k získání standardního výstupu, chybového výstupu a k zadání vstupu do příkazu. Vrací n-tici obsahující výstup a chybu. Podívejme se na příklad:
import subprocess
process = subprocess.Popen(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
result = process.communicate()
print(result)
`subprocess.Popen()` – Vstup
Vstup nelze předat přímo třídě `Popen`. K zadání vstupu do příkazu musíme použít argument `stdin`. Instance třídy `Popen` nám poskytne objekt `stdin`. Má metodu `write()`, kterou použijeme pro zadání vstupu do příkazu.
Jak již bylo zmíněno, vstup je ve výchozím nastavení považován za bajty. Proto nezapomeňte nastavit argument `text` na `True` při vytváření instance `Popen`.
Podívejme se na příklad:
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()` slouží ke kontrole, zda bylo provádění příkazu dokončeno. Pokud se příkaz stále provádí, metoda vrací `None`. Podívejme se na příklad:
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
V uvedeném kódu používáme příkaz `ping` s 5 požadavky. Nekonečná smyčka se opakuje, dokud není dokončeno provádění příkazu. Ke kontrole stavu provádění příkazu používáme metodu `poll()`. Pokud `poll()` vrátí jinou hodnotu než `None`, provádění je dokončeno a nekonečná smyčka se přeruší.
Spouštění Bash Skriptů
Nyní se podíváme, jak spouštět bash skripty v Pythonu, když jsme si již ukázali dva způsoby provádění příkazů.
Modul `subprocess` obsahuje metodu `call()`, která slouží k provádění bash skriptů. Tato metoda vrací výstupní kód z bash skriptu. Výchozí výstupní kód pro bash skripty je 0. Podívejme se na příklad.
Vytvořte bash skript s názvem `practice.sh` s následujícím obsahem:
#!/bin/bash
echo "Hello, World!"
exit 1
Nyní napište Python skript, který spustí výše uvedený bash skript:
import subprocess
exit_code = subprocess.call('./practice.sh')
print(exit_code)
Po spuštění tohoto Python skriptu uvidíte následující výstup:
Hello, World!
1
Závěr
Ukázali jsme si, jak spouštět bash příkazy a skripty v Pythonu. Můžete je využít pro efektivnější automatizaci úloh.
Příjemné kódování 👨💻