Як запускати сценарії bash за допомогою Python?

Якщо ви користуєтеся операційною системою Linux, ви, безсумнівно, оціните можливості команд оболонки.

Коли ви працюєте з Python, ви, можливо, вже розглядали варіанти автоматизації певних процесів. Це, безумовно, ефективний спосіб заощадити час. Ви також можете використовувати сценарії bash для автоматизації задач.

Python є більш зручним для написання сценаріїв, ніж bash, а управління сценаріями Python є більш простим у порівнянні зі сценаріями bash. Підтримка сценаріїв bash може стати складною задачею, особливо коли вони збільшуються в розмірах.

Але що робити, якщо у вас вже є сценарії bash, які ви хочете запускати за допомогою Python?

Чи існує спосіб інтегрувати виконання команд і сценаріїв bash безпосередньо у ваші Python-скрипти?

Так, у Python є вбудований модуль, що називається subprocess, який спеціально призначений для виконання команд і сценаріїв у Python-програмах. Давайте розглянемо детальніше, як використовувати цей модуль для запуску команд та скриптів bash з ваших Python-програм.

Виконання команд Bash у Python

Як ви вже могли здогадатися, модуль subprocess є ключовим інструментом для виконання команд і сценаріїв bash. Він пропонує різноманітні методи та класи для цієї мети.

Основними компонентами модуля subprocess є метод run() і клас Popen(). Саме вони забезпечують можливість виконання команд bash у Python-скриптах. Розглянемо кожен з них окремо.

Метод subprocess.run()

Метод subprocess.run() приймає список рядків як позиційний аргумент. Це обов’язковий параметр, оскільки він містить команду bash та її аргументи. Перший елемент списку – це ім’я команди, а решта – це аргументи до неї.

Розглянемо короткий приклад:

import subprocess
subprocess.run(["ls"])

Цей сценарій виведе перелік всіх файлів і директорій у поточному робочому каталозі, де розміщено скрипт. У наведеному вище прикладі команда не має аргументів. Ми надали тільки саму команду bash. Ми можемо додати додаткові аргументи до команди ls, наприклад, -l, -a, -la і т.д.

Давайте подивимося на приклад із аргументами команди:

import subprocess
subprocess.run(["ls", "-la"])

Ця команда відобразить всі файли, включаючи приховані, а також їхні права доступу. Аргумент -la задає відображення додаткової інформації, включаючи приховані файли та директорії.

Під час написання команд можуть виникати помилки. Якщо ви хочете їх перехопити та обробити пізніше, то це можна зробити за допомогою ключового аргументу stderr.

Розглянемо приклад:

import subprocess
result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True)
print(result.stderr)

Переконайтеся, що у вашому робочому каталозі немає файлу під назвою sample.txt. Значення ключового аргументу stderr встановлено на PIPE, що дозволяє повернути повідомлення про помилку в об’єкт. Ви потім можете отримати доступ до цього повідомлення за допомогою атрибута stderr. А text=True вказує, що результат має бути рядком.

Аналогічно, ви можете отримати вихід команди, використовуючи ключовий аргумент stdout:

import subprocess
result = subprocess.run(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(result.stdout)

subprocess.run() – Вхідні дані

Ви також можете передавати вхідні дані командам за допомогою ключового аргументу input. Вхідні дані потрібно передавати у вигляді рядка. Тому важливо встановити значення True для ключового аргументу text, інакше за замовчуванням вхідні дані будуть прийняті як байти.

Розглянемо приклад:

import subprocess
subprocess.run(["python3", "add.py"], text=True, input="2 3")

У цьому прикладі скрипт Python add.py отримує два числа як вхідні дані. Ми передали ці дані за допомогою ключового аргументу input.

Клас subprocess.Popen()

Клас subprocess.Popen() є більш розширеним, ніж метод subprocess.run(). Він дає більше можливостей для керування процесами, такими як отримання статусу виконання, виводу, передачі вхідних даних тощо.

Існує кілька методів класу subprocess.Popen(), які вам необхідно знати. Розглянемо їх один за одним разом із прикладами коду.

Метод wait()

Метод wait() використовується для очікування завершення виконання команди. Наступні рядки Python-коду не виконуватимуться до завершення попередньої команди, до якої застосовується метод wait. Розглянемо приклад:

import subprocess
process = subprocess.Popen(["ls", "-la"])
print("Виконано!")

Запустіть цей код і подивіться на результат. Ви побачите, що “Виконано!” буде надруковано перед виконанням команди ls -la. Метод wait() дозволяє уникнути цього, дочекавшись завершення виконання команди:

import subprocess
process = subprocess.Popen(["ls", "-la"])
process.wait()
print("Виконано!")

Тепер ви побачите, що повідомлення “Виконано!” з’явиться тільки після завершення виконання команди.

Метод communicate()

Метод communicate() використовується для отримання виводу, повідомлень про помилки та передачі вхідних даних до команди. Він повертає кортеж, який містить вивід та помилки. Розглянемо приклад:

import subprocess
process = subprocess.Popen(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
result = process.communicate()
print(result)

subprocess.Popen() – Вхідні дані

Не можна передати вхідні дані класу Popen безпосередньо. Потрібно скористатися ключовим аргументом stdin для передачі вхідних даних команді. Екземпляр класу Popen надає об’єкт stdin, який має метод write, що використовується для передачі даних. За замовчуванням, write приймає дані у вигляді байтових об’єктів, тому важливо вказати text=True при створенні екземпляра Popen.

Розглянемо приклад:

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()

Метод poll() використовується для перевірки, чи завершилося виконання команди. Він поверне None, якщо команда ще виконується. Розглянемо приклад:

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

У цьому коді ми використовуємо команду ping із 5 запитами. Цикл while повторюється, поки виконання команди не буде завершено. Метод poll перевіряє статус виконання команди. Якщо poll повертає значення, відмінне від None, це означає, що виконання завершено і цикл переривається.

Виконання сценаріїв Bash

Ми розглянули два способи виконання команд. Тепер давайте подивимося, як запускати сценарії bash з Python.

У subprocess є метод call(), який спеціально призначений для виконання сценаріїв bash. Цей метод повертає код виходу зі сценарію bash. Стандартний код виходу для сценаріїв bash – це 0. Розглянемо приклад.

Створіть сценарій bash з назвою practice.sh з наступним вмістом:

#!/bin/bash

echo "Hello, World!"
exit 1

Тепер напишіть сценарій Python, який виконає цей bash-скрипт:

import subprocess
exit_code = subprocess.call('./practice.sh')
print(exit_code)

Ви отримаєте наступний результат при запуску цього Python-скрипту:

Hello, World!
1

Висновок

Ми розглянули способи виконання команд та сценаріїв bash у Python. Ви можете використовувати ці знання для більш ефективної автоматизації ваших задач.

Бажаємо вам успішного кодування! 👨‍💻