Введение
При работе с системами часто требуется вызывать существующие системные команды даже при написании программ на Python. Поэтому важно понимать, какие средства Python предназначены для выполнения процессов.
Модуль subprocess позволяет Python запускать другие программы, получать их вывод и безопасно проверять, завершились ли они успешно. Это стандартный способ связать код Python с командами оболочки.
В этой лабораторной работе вы научитесь:
- Импортировать модуль
subprocess. - Запускать простую команду Linux (
ls -l) из Python. - Сохранять вывод команды для дальнейшей обработки.
- Декодировать вывод из байтов в читаемую строку.
- Корректно обрабатывать ошибки при сбое команды.
Все действия будут выполняться в каталоге ~/project в среде Linux.
Импорт модуля subprocess
На этом шаге вы создадите скрипт Python и импортируете модуль, необходимый для взаимодействия с системной оболочкой.
Модуль subprocess входит в стандартную библиотеку Python, поэтому устанавливать дополнительные компоненты не нужно. Он позволяет скрипту Python взаимодействовать с операционной системой.
Это стандартный мост между кодом Python и существующими инструментами Unix. При работе с системами такой мост особенно важен: обычно невозможно сразу заменить все команды оболочки. Чаще Python выступает координатором — вызывает системные инструменты, получает их результаты и добавляет более высокоуровневую логику.
Сначала перейдите в нужный каталог:
cd ~/project
Теперь создайте новый файл Python с именем list_files.py с помощью редактора nano или удобным для вас способом:
nano list_files.py
Добавьте в начало файла следующую строку, чтобы импортировать модуль:
import subprocess
print("Module imported successfully")
Сохраните файл и выйдите из редактора (в nano нажмите Ctrl+X, затем Y, затем Enter).
Теперь запустите скрипт, чтобы проверить его работу:
python3 list_files.py
На экране должно появиться сообщение "Module imported successfully".
Запуск команды
На этом шаге вы измените скрипт, чтобы он действительно выполнял команду Linux. Мы воспользуемся функцией subprocess.run(), которая появилась в Python 3.5 и является рекомендуемым способом запуска дочерних процессов.
Когда Python запускает другую программу, создаётся отдельный процесс. У этого дочернего процесса есть собственный код завершения, стандартный вывод и стандартный поток ошибок — так же, как у команды, запущенной непосредственно в оболочке. Понимание этой границы между процессами упрощает последующую обработку ошибок.
Снова откройте list_files.py:
nano list_files.py
Измените содержимое файла так, чтобы оно выглядело следующим образом. Команда ls -l выведет подробный список файлов в текущем каталоге:
import subprocess
## Run the 'ls -l' command
## The command and its arguments are passed as a list of strings
subprocess.run(["ls", "-l"])
Обратите внимание: команда передаётся в виде списка ["ls", "-l"]. Это безопаснее и удобнее, чем передавать одну строку, поскольку такой подход позволяет избежать проблем с внедрением команд и пробелами в именах файлов.
Сохраните файл и выйдите из редактора.
Теперь запустите скрипт:
python3 list_files.py
В терминале должен появиться вывод команды ls -l. Это происходит потому, что по умолчанию subprocess.run отправляет вывод команды в стандартный вывод, то есть на экран.
Получение вывода
На предыдущем шаге вывод сразу отображался на экране. Однако в сценариях автоматизации обычно требуется сохранить вывод в переменной, чтобы код Python мог обработать его, например разобрать имена файлов или проверить наличие ошибок.
В этом заключается разница между простым запуском команды и её интеграцией в программу. После сохранения вывода в виде данных скрипт может принимать решения на его основе, а результат не остаётся только в терминале для просмотра пользователем.
Для этого нужно использовать аргумент capture_output=True.
Снова откройте list_files.py:
nano list_files.py
Измените код так, чтобы он сохранял результат:
import subprocess
## Run the command and capture the output
result = subprocess.run(["ls", "-l"], capture_output=True)
## Print the captured result object
print(result)
Сохраните файл и выйдите из редактора.
Запустите скрипт:
python3 list_files.py
Вывод будет выглядеть примерно так:
CompletedProcess(args=['ls', '-l'], returncode=0, stdout=b'total 0\n-rw-rw-r-- 1 labex labex 0 ...', stderr=b'')
Это объект CompletedProcess. Обратите внимание на часть stdout=b'...'. Буква b означает bytes — байты. Это значит, что Python получил необработанные данные в том виде, в котором их видит компьютер, а не читаемый текст (строку). На следующем шаге мы преобразуем эти данные в текст, понятный человеку.
Декодирование вывода
Поскольку вывод получен в виде байтов, его нужно декодировать в обычную строку Python в кодировке UTF-8, чтобы текст можно было прочитать и использовать.
Различие между байтами и текстом — фундаментальная особенность Python. В конечном счёте компьютеры передают необработанные байты, тогда как пользователю обычно нужен текст. Многие непонятные ошибки в сценариях автоматизации возникают из-за того, что текущий формат данных не учитывается.
Снова откройте list_files.py:
nano list_files.py
Измените код: обратитесь к атрибуту stdout и декодируйте его содержимое:
import subprocess
## Run the command and capture the output
result = subprocess.run(["ls", "-l"], capture_output=True)
## Decode the bytes to a string
output_string = result.stdout.decode("utf-8")
## Print the clean string
print("--- Command Output ---")
print(output_string)
Сохраните файл и выйдите из редактора.
Запустите скрипт:
python3 list_files.py
Теперь должен появиться отформатированный вывод команды ls -l, такой же, как при непосредственном запуске команды в оболочке, но на этот раз его выводит Python.
Примечание: вместо этого можно передать в subprocess.run() аргумент text=True (или universal_newlines=True), чтобы Python автоматически выполнил декодирование. Однако понимание метода .decode() важно для работы с двоичными данными.
Обработка ошибок
Что произойдёт, если команда завершится ошибкой? Например, если попытаться вывести список каталога, которого не существует. Хорошие скрипты должны обрабатывать такие ошибки.
В Unix-подобных системах сбой команды обычно обозначается ненулевым статусом завершения. Вызов subprocess.run(..., check=True) преобразует это соглашение операционной системы в исключение Python. Благодаря этому скрипт может реагировать на ошибку с помощью обычной логики Python.
Создадим новый скрипт handle_error.py, чтобы продемонстрировать этот подход.
nano handle_error.py
Добавьте следующий код. Мы попытаемся вывести список несуществующего каталога /nonexistent. Параметр check=True указывает Python создать исключение CalledProcessError, если команда завершится с ненулевым кодом, который означает ошибку.
import subprocess
try:
## Try to list a directory that does not exist
## check=True raises an exception if the command fails
subprocess.run(["ls", "/nonexistent"], check=True, capture_output=True)
except subprocess.CalledProcessError as e:
print("An error occurred!")
print(f"Return code: {e.returncode}")
## We can also print the error message generated by the command (stderr)
print(f"Error message: {e.stderr.decode('utf-8')}")
Сохраните файл и выйдите из редактора.
Запустите скрипт:
python3 handle_error.py
Должен появиться вывод, показывающий, что ошибка была перехвачена:
An error occurred!
Return code: 2
Error message: ls: cannot access '/nonexistent': No such file or directory
Если бы мы не использовали блок try-except, программа завершилась бы с длинной трассировкой ошибки. Такой подход позволяет скрипту обнаружить сбой и решить, что делать дальше: например, записать ошибку в журнал, повторить операцию или корректно завершить работу.
Итоги
В этой лабораторной работе вы изучили основы выполнения команд оболочки с помощью модуля subprocess в Python. Вы научились:
- Импортировать модуль
subprocess. - Запускать команды с помощью
subprocess.run(). - Получать стандартный вывод с помощью
capture_output=True. - Декодировать байтовый вывод в читаемые строки.
- Обрабатывать сбои команд с помощью
check=Trueи блоковtry-except.
Эти навыки необходимы для создания сценариев автоматизации, инструментов развёртывания и утилит управления системами на Python.



