Сравнение методов, функций и выходных данных.

Возможность выполнять команды оболочки в 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.