Jak spouštět bash skripty pomocí Pythonu?

Photo of author

By etechblogcz

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í 👨‍💻