subprocess в Python

Содержание
Введение
Примеры в Linux и Windows
Обработка ошибок
Bash команда с опциями
Передать переменную в аргумент команды
args, returncode, stdout
Передача аргументов в скрипт
Логи с помощью subprocess
Сравнить два файла
Определить версию Linux
.Popen()
Похожие статьи

Введение

В этой статье вы узнаете как выполнять команды Linux и Windows из кода на Python 3.

Создайте файл subprocess_demo.py и копируйте туда код из примеров.

Запустить скрипт можно командой

python subprocess_demo.py

Официальная документация

Простой пример в Linux

Это пример для Linux. Простой пример для Windows здесь

Пример программы, которая выполняет Linux команду ls

import subprocess subprocess.run('ls')

Простой пример в Windows

Это пример для Windows. Простой пример для Linux здесь

Пример программы, которая выполняет в Windows команду dir

import subprocess subprocess.run('dir', shell=True)

Bash команда с опциями

Чтобы выполнить Bash команду с опциями, например, ls - la нужно добавить shell=True

import subprocess subprocess.run('ls -la', shell=True)

У использования shell=True есть одна важная особенность: нужно особенно внимательно следить за безопастностью.

Рекомендуется использовать shell=True только если вы передаёте параметры самостоятельно.

Избежать использования shell=True можно передав команду и параметры списком:

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

Передать переменную в аргумент команды

По аналогии с предыдущим параграфом - в качестве аргумента можно использовать и переменную

import subprocess text = "Visit TopBicycle.ru to support my website" subprocess.run(["echo", text])

python3 subprocess_demo.py

Visit TopBicycle.ru to support my website

args, returncode, stdout

Разберём subprocess более подробно

import subprocess p1 = subprocess.run(['ls', '-la']) print("p1") print(p1) print("p1.args") print(p1.args) print("p1.returncode") print(p1.returncode) print("p1.stdout") print(p1.stdout)

python3 subprocess_demo.py

total 12 drwxrwxr-x 2 andrei andrei 4096 Nov 30 17:57 . drwxrwxr-x 3 andrei andrei 4096 Nov 30 17:57 .. -rw-rw-r-- 1 andrei andrei 195 Nov 30 16:51 subprocess_demo.py p1 CompletedProcess(args='ls -la', returncode=0) p1.args ls -la p1.returncode 0 p1.stdout None

Чтобы не выводить результат в терминал а сохранить в переменную, нужно воспользоваться опцией capture_output=True - доступна, начиная с версии Python 3.7

import subprocess p1 = subprocess.run(['ls', '-la'], capture_output=True) print(p1.stdout)

python3 subprocess_demo.py

b'total 12\ndrwxrwxr-x 2 andrei andrei 4096 Nov 30 18:41 .\ndrwxrwxr-x 3 andrei andrei 4096 Nov 30 18:40 ..\n-rw-rw-r-- 1 andrei andrei 92 Nov 30 18:41 subprocess_demo.py\n'

Если byte вывод вам не нравится его можно декодировать

import subprocess p1 = subprocess.run(['ls', '-la'], capture_output=True) print(p1.stdout.decode())

python3 subprocess_demo.py

total 12 drwxrwxr-x 2 andrei andrei 4096 Nov 30 18:41 . drwxrwxr-x 3 andrei andrei 4096 Nov 30 18:40 .. -rw-rw-r-- 1 andrei andrei 101 Nov 30 18:46 subprocess_demo.py

Или можно использовать опцию text=True

import subprocess p1 = subprocess.run(['ls', '-la'], capture_output=True, text=True) print(p1.stdout)

total 12 drwxrwxr-x 2 andrei andrei 4096 Nov 30 18:41 . drwxrwxr-x 3 andrei andrei 4096 Nov 30 18:40 .. -rw-rw-r-- 1 andrei andrei 101 Nov 30 18:46 subprocess_demo.py

Ещё один вариант перенаправления вывода stdout=subprocess.PIPE

import subprocess p1 = subprocess.run(['ls', '-la'], stdout=subprocess.PIPE, text=True) print(p1.stdout)

python3 subprocess_demo.py

total 12 drwxrwxr-x 2 andrei andrei 4096 Nov 30 18:41 . drwxrwxr-x 3 andrei andrei 4096 Nov 30 18:40 .. -rw-rw-r-- 1 andrei andrei 101 Nov 30 18:46 subprocess_demo.py

Вывод в файл

import subprocess with open('output.txt', 'w') as f: p1 = subprocess.run(['ls', '-la'], stdout=f, text=True)

Обработка ошибок

Добавим заведомо неверное условие в команду. Например, пусть листинг выполняется не для текущей директории а для несуществующей.

import subprocess p1 = subprocess.run(['ls', '-la', 'not_exist'], capture_output=True, text=True) print(p1.returncode) print(p1.stderr)

2 ls: cannot access 'not_exist': No such file or directory

Обратите внимане, что Python в этом примере не выдаёт никаких ошибок

Чтобы Python информировал об ошибках во внешних командах используйте опцию check=True

import subprocess p1 = subprocess.run(['ls', '-la', 'not_exist'], capture_output=True, text=True, check=True) print(p1.returncode) print(p1.stderr)

python3 subprocess_demo.py

Traceback (most recent call last): File "subprocess_demo.py", line 3, in <module> p1 = subprocess.run(['ls', '-la', 'not_exist'], capture_output=True, text=True, check=True) File "/usr/lib/python3.8/subprocess.py", line 512, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['ls', '-la', 'not_exist']' returned non-zero exit status 2.

Обратите внимане, что теперь Python выдаёт ошибку, а до print(p1.returncode) и print(p1.stderr) дело уже не доходит

import subprocess p1 = subprocess.run(['ls', '-la', 'not_exist'], stderr=subprocess.DEVNULL) print(p1.stderr)

python3 subprocess_demo.py

None

Передача аргументов в скрипт

Допустим, нужно вызвать скрипт с несколькими аргументами

import subprocess subprocess.call(['./script.sh %s %s %s' %(ip, username, test_time)], shell=True)

Ещё пример: из python скрипта вызвать sed и обрезать число строк, которое сам скрипт получает как аргумент

import subprocess LINES = int(sys.argv[1]) subprocess.call(['sed -i -e 1,%sd 2025-07-08-log.txt' %(LINES)], shell=True)

Эту задачу можно решить на чистом Python решение

with open('file_with_lines.txt', 'r') as fin: data = fin.readlines()[3:] with open('file_with_lines.txt', 'w') as fout: fout.writelines(data)

Логи с помощью subprocess

Если запускать код в какой-то среде, где лог в файл неудобен а лог с помощью print невозможен, можно использовать echo из bash

import subprocess text = "Andrei Log: robot/src/libraries/TestController.py is running" subprocess.run(["echo", text])

РЕКЛАМА от Яндекса. Может быть недоступна в вашем регионе

Конец рекламы. Если там пусто считайте это рекламой моей телеги

Сравнить два файла

Если запускать код в какой-то среде, где лог в файл неудобен а лог с помощью print невозможен, можно использовать echo из bash

import subprocess def compare(file1, file2): subprocess.run(["diff", file1, file2])

Определить версию Linux

С помощью subprocess можно в том числе определить версию Linux

В примере ниже определяются CentOS, RedHat, Rocky, Ubuntu

import subprocess import sys CENTOS = {"os_name": "CentOS", "cmd": "rpm --eval %{centos_ver}"} REDHAT = {"os_name": "Red", "cmd": "rpm --eval %{red_hat_ver}"} ROCKY = {"os_name": "Rocky", "cmd": "rpm --eval %{rocky_ver}"} UBUNTU = {"os_name": "Ubuntu", "cmd": "cat /etc/issue"} OS_LIST = [CENTOS, REDHAT, ROCKY, UBUNTU] def find_os() -> object: try: p = subprocess.run(["lsb_release", "-a"], capture_output=True, text=True) except Exception as e: print(f"lsb_release -a call failed: {e!r}", file=sys.stderr) raise system_release = str(p.stdout) + str(p.stderr) system_release = system_release.split() for os in OS_LIST: name = os["os_name"] if name in system_release: break else: os = None return os def get_name(os) -> str: name = os["os_name"] return name def get_version(os) -> str: cmd = os["cmd"] cmd = cmd.split() p = subprocess.run(cmd, capture_output=True, text=True) version = str(p.stdout) try: version = int(version) except: version = version.split() version = version[1] return version def get_linux_version() -> tuple: os = find_os() if os is not None: name = get_name(os) version = get_version(os) linux_version = (name, version) else: print("os is not found") linux_version = (None, None) return linux_version if __name__ == '__main__': print(get_linux_version())

subprocess.Popen()

sp_demo/ |-- app.py |-- demo.py `-- output.txt

# app.py _input = input("enter a string: ") _output = "hello " + _input with open("output.txt", "w") as f: f.write(_output)

# demo.py import sys import os.path import subprocess app_path = os.path.join(os.path.dirname(__file__), "app.py") command = [sys.executable, app_path] process = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) # Передадим в input скрипта app.py строку # с помощью метода communicate() process.communicate(input="www.devhops.ru\n".encode())

python demo.py
cat output.txt

hello www.devhops.ru

.communicate()

# demo.py import sys import os.path import subprocess app_path = os.path.join(os.path.dirname(__file__), "app.py") command = [sys.executable, app_path] process = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) out, err = process.communicate(input="www.devhops.ru\n".encode()) if process.returncode != 0: print(f"out: {out}") print(f"err: {err}")

python demo.py
cat output.txt

hello www.devhops.ru

Изучим как будет обрабатываться ошибка, например, обращение к несуществующему файлу

# … process = subprocess.Popen("python app1.py", # …

out: b'' err: b"C:\Users\A\AppData\Local\Programs\Python\Python312\python.exe: can't open file 'C:\\sp_demo\\app1.py': [Errno 2] No such file or directory\r\n"

Обратите внимание, что сейчас в стандартном выводе пусто, зато есть ошибка в stderr, которая остаётся в PIPE и потом с помощью communicate передаётся в err.

Оставим ошибку в имени app1.py и заменим stderr с PIPE на STDOUT

# demo.py import sys import os.path import subprocess app_path = os.path.join(os.path.dirname(__file__), "app1.py") command = [sys.executable, app_path] process = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True ) out, err = process.communicate(input="www.devhops.ru\n".encode()) if process.returncode != 0: print(f"out: {out}") print(f"err: {err}")

out: b"C:\Users\A\AppData\Local\Programs\Python\Python312\python.exe: can't open file 'C:\\sp_demo\\app1.py': [Errno 2] No such file or directory\r\n" err: None

Обратите внимание на то, что в err, из communicate теперь ничего нет. Ошибка теперь отравляется в stdout и оттуда попадает из communicate в out

Запустить неблокирующий процесс

Если нужно из одного скрипта сначала запустить процесс, который занимает вывод, а потом, например, обращаться к его API поможет флаг close_fds

subprocess.Popen("C:\Server.exe", close_fds=True)

Автор статьи: Андрей Олегович

Похожие статьи
Функции
try except
*args **kwargs
Работа с REST API на Python
Файлы: записать, прочитать, дописать, контекстный менеджер…
Работа с Базами Данных
datetime: Дата и время в Python
json.dumps
Виртуальное окружение
subprocess: выполнение bash команд из Python
multiprocessing: несколько процессов одновременно
psutil: cистемные ресурсы
sys.argv: аргументы командной строки

РЕКЛАМА хостинга Beget, которым я пользуюсь более десяти лет

Изображение баннера

Конец рекламы хостинга Beget, который я всем рекомендую.

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: