Сравнение методов, функций и выходных данных.
Возможность выполнять команды оболочки в Python увеличивает гибкость и возможности применения языка. Однако количество способов, которыми это возможно, может быть ошеломляющим. Этот пост призван раскрыть поведение этих функций и помочь разработчикам принимать обоснованные решения относительно своего кода.
Обзор
Что такое «оболочка»?
Оболочка – это программа, которая принимает команды, предоставленные пользователем, и отправляет их операционной системе для выполнения. Часто это интерпретируемый язык со своим собственным синтаксисом и требованиями к вводу.
Сегодня компьютеры имеют графические интерфейсы, которые позволяют пользователям взаимодействовать с оболочкой. На хосте Linux это будет Terminal
, предоставляющий доступ к оболочке /bin/bash
или /bin/sh
. Принимая во внимание, что Windows использует Command Prompt
для взаимодействия с MS-DOS
.
Зачем выполнять команды оболочки в Python?
Использование Python для выполнения команд оболочки позволяет автоматизировать повторяющиеся задачи на уровне ОС. Этот подход также можно использовать для объединения программ на альтернативных языках для внедрения логики и управления.
Выполнение команд с ОС
Стандартный модуль Python os
устанавливается по умолчанию и предоставляет портативный способ использования функций, зависящих от операционной системы, включая: выполнение команд, взаимодействие с файловыми системами и многое другое.
ОС.система()
os.system()
Выполняет команды оболочки, вызывая стандартную C
функцию system()
. Он принимает пользовательский ввод, предоставленный в виде строки, и выполняет команду в рамках нового дочернего процесса. Затем статус выхода операционной системы возвращается пользователю.
>>> import os >>> r = os.system('whoami') root >>> r 0
За: полезно, когда результаты выполнения не нужны для принятия дополнительных решений. Разработчики могут строить логику вокруг индикации операционной системой нормального или аварийного завершения.
Против: Как показано, os.system()
не возвращает фактический вывод выполняемой команды.
os.popen()
os.popen()
— это еще один метод выполнения команд оболочки, но он отличается от os.system
тем, что пользователи могут получить вывод.
Это делается путем открытия канала к команде или от нее — эти режимы могут быть определены пользователем с помощью необязательного аргумента mode=
. В результате возвращается файловый объект, подключенный к каналу, который позволяет пользователю получить результаты:
>>> import os >>> r = os.open('whoami') >>> r.read() 'root\n'
За: пользователи могут получить вывод команды.
Против: Согласно документации ОС, os.popen()
реализуется с использованием модуля subprocess
, который является предпочтительным методом и предлагает дополнительные функции.
Выполнение команд с подпроцессом
subprocess
— это стандартный модуль Python, который позволяет пользователям создавать новые процессы и подключаться к каналам ввода/вывода/ошибок.
Это считается современным способом выполнения команд оболочки и специально разработано для замены старых модулей и функций, в том числе: os.system
и os.popen
(PEP 324).
подпроцесс.Popen()
Класс subprocess.Popen()
работает, выполняя команды оболочки, предоставленные в виде последовательности аргументов, в новом дочернем процессе. Хотя выходные данные выполнения не возвращаются по умолчанию, класс имеет несколько отличий, которые позволяют пользователям настраивать его производительность и управлять стандартным вводом/выводом/ошибкой:
>>> from subprocess import Popen, PIPE >>> r = Popen(['whoami'], stdout=PIPE, stderr=PIPE) >>> r.communicate()[0] b'root\n' >>> r.returncode 0
Аргументы (необязательно)
shell=False
— Аргумент оболочки указывает, должна ли команда интерпретироваться оболочкой.
Примечание. Добавлениеshell=True
также позволяет задавать команды в виде строковых значений.stdin=None
,stdout=None
,stderr=None
— передать стандартный ввод, вывод и ошибку в файловый объект. В качестве альтернативы можно сделать эти значения равнымиsubprocess.PIPE
для записи в переменную (как показано выше).user=None
— введите имя пользователя илиuid
для выполнения команд оболочки в качестве альтернативного пользователя.pwd.getpwnam().pw_uid
используется для преобразования строк имени пользователя, а значенияuid
передаются в системный вызовsetreuid()
.
Pro: предоставляет надежные возможности и настройки. Кроме того, класс Popen является основной движущей силой многих обсуждаемых последующих методов.
Против: не передает выходные данные выполнения автоматически в переменную. Ручное определение объектов выходного файла, каналов и communicate()
может сделать этот метод более «сложным» для новичков.
подпроцесс.run()
subprocess.run()
— это еще один метод выполнения команд оболочки, но он обеспечивает более простую реализацию по сравнению с прямым вызовом класса Popen()
. Команды вводятся как последовательность в функцию, выполняются, и возвращается объект subprocess.CompletedProcess()
:
>>> from subprocess import run, PIPE >>> r = run(['whoami'], stdout=PIPE) >>> r.stdout b'root\n' >>> r.returncode 0
Аргументы (необязательно)
check=False
— если включено, будет возбуждено исключениеCalledProcessError
, если команда вернет ненулевой код выхода.capture_output=False
— stdout и stderr будут захвачены с использованием внутреннего объектаPopen
сstdout=PIPE
иstderr=PIPE
.timeout=None
— Предоставляет возможность установить тайм-аут для данной команды. После достижения дочерний процесс уничтожается и возникает исключениеTimeoutExpired
.text=None
— вернет stdout и stderr в виде строковых значений.
За: Рекомендуемый подход к вызову подпроцессов. Поскольку он использует Popen()
за кулисами, он поддерживает многие из тех же необязательных аргументов и функций командной строки.
Против: по умолчанию этот подход не фиксирует выходные данные выполнения и требует capture_output=True
или stdout=subprocess.PIPE
.
подпроцесс.вызов()
subprocess.call()
работает аналогично os.system()
, в том смысле, что команда задается как последовательность аргументов, и возвращается только атрибут returncode
.
>>> import subprocess >>> r = subprocess.call(['whoami']) root >>> r 0
За: быстрое и простое выполнение команд оболочки. поддерживает аргументы shell
и timeout
, указанные в subprocess.run()
.
Против. Хотя stdout
и stderr
указаны как необязательные аргументы, в документации указано, что их не следует использовать. Следовательно, этот метод может возвращать только статус выхода команды.
подпроцесс.getoutput()
Последними методами выполнения команд оболочки являются subprocess.getoutput()
и subprocess.getstatusoutput()
. Обе являются устаревшими функциями, перенесенными из Python 2.x.
Эти функции просто выполняют строковую команду с Popen.check_output()
и возвращают результат в виде строкового значения. Как показано, getstatusoutput
отличается тем, что возвращает кортеж, содержащий код выхода плюс результат:
>>> import subprocess >>> r= subprocess.getoutput(‘whoami’) >>> r ‘root’ >>> r2 = subprocess.getstatusoutput(‘whoami’) >>> r2 (0, ‘root’)
За: getoutput
и getstatusoutput
просты и удобны в использовании. Выходные данные выполнения возвращаются в определяющую переменную для продолжения логики.
Против: ограниченные возможности или контроль над функциями — по сравнению с другими subprocess
обсуждаемыми методами.
Спасибо за прочтение! Узнайте больше обо мне на m8sec.dev и подпишитесь на другие материалы Pythonic.