Выбрать главу

Например, чтобы запустить простую команду оболочки, как мы делали это с помощью функции os.system выше, можно воспользоваться функцией call из нового модуля, которая действует похожим образом (чтобы запустить такую команду, как type, встроенную в оболочку Windows, требуется соблюсти дополнительные условия, хотя для запуска обычных выполняемых файлов, таких как python, этого не требуется):

>>> import subprocess

>>> subprocess.call('python helloshell.py')    # напоминает os.system()

The Meaning of Life 0

>>> subprocess.call('cmd /C "type helloshell.py”') # встроенная команда

#    a Python program print(‘The Meaning of Life’)

0

>>> subprocess.call('type helloshell.py', shell=True) # альтернативный способ

#    a Python program    # для встроенных команд

print(‘The Meaning of Life’)

0

Обратите внимание на аргумент shell=True в последнем вызове. Это платформозависимая особенность:

•    Чтобы запустить встроенную команду оболочки в Windows, инструментам модуля subprocess, таким как call и Popen (об этой функции будет рассказываться ниже), необходимо передавать аргумент shell=True. Команды Windows, такие как type, требуют соблюдения дополнительных условий, но для запуска обычных выполняемых файлов, таких как python, этого не требуется.

•    В Unix-подобных платформах, когда аргумент shell принимает значение False (по умолчанию), команда запускается непосредственно вызовом функции os.execvp, с которой мы встретимся в главе 5. Если в этом аргументе передать True, команда будет выполнена с помощью оболочки, при этом вы можете указать используемую оболочку в дополнительном аргументе.

Подробнее о некоторых из этих особенностей мы поговорим ниже, а пока достаточно будет запомнить, что в Unix-подобных системах вам может потребоваться передавать аргумент shell=True в некоторых примерах в этом разделе и в книге, если они предполагают использование таких особенностей оболочки, как путь поиска программ. Поскольку я запускаю примеры в Windows, этот аргумент я часто буду опускать.

Помимо имитации функции os.system, мы точно так же можем использовать этот модуль для имитации функции os.popen, чтобы запускать команды оболочки и получать ее вывод в нашем сценарии:

>>> pipe = subprocess.Popen('python helloshell.py', stdout=subprocess.PIPE)

>>> pipe.communicate()

(b’The Meaning of Life\r\n’, None)

>>> pipe.returncode

0

Здесь мы связали поток стандартного вывода команды оболочки с каналом и вызвали метод communicate, ожидающий завершения команды и принимающий текст, который она выводит в стандартный поток вывода и в стандартный поток ошибок. Код завершения команды доступен в виде атрибута, после того как она будет выполнена. Точно так же мы могли бы использовать отдельную функцию чтения потока стандартного вывода команды и отдельную функцию ожидания ее завершения (которая возвращает код завершения):

>>> pipe = subprocess.Popen('python helloshell.py', stdout=subprocess.PIPE)

>>> pipe.stdout.read()

b’The Meaning of Life\r\n’

>>> pipe.wait()

0

Фактически существует возможность прямой замены вызова os.popen объектом subprocess.Popen:

>>> from subprocess import Popen, PIPE

>>> Popen('python helloshell.py', stdout=PIPE).communicate()[0]

b’The Meaning of Life\r\n’

>>>

>>> import os

>>> os.popen('python helloshell.py').read()

‘The Meaning of Life\n’

Как видите, реализация относительно простых случаев с помощью модуля subprocess требует дополнительной работы. Но ситуация меняется в лучшую сторону, когда возникает необходимость гибкого управления потоками ввода-вывода. Фактически, благодаря возможности обрабатывать стандартные потоки вывода и ошибок команды похожими способами, модуль subprocess в Python 3.X заменил оригинальные функции os.popen2, os.popen3 и os.popen4, имевшиеся в Python 2.X. Теперь эти функции являются лишь частными случаями использования интерфейса объектов модуля subprocess. Поскольку в более сложных случаях использования этого модуля предполагается взаимодействие со стандартными потоками ввода-вывода, мы отложим дальнейшее обсуждение этого модуля, пока не познакомимся с механизмом перенаправления потоков в следующей главе.

Ограничения, присущие командам оболочки

Прежде чем двинуться дальше, вы должны запомнить два ограничения, связанные с функциями system и popen. Во-первых, хотя сами по себе эти функции хорошо переносимы, в действительности их применение переносимо лишь в той мере, в какой это относится к выполняемым ими командам. Предыдущие примеры с командами DOS dir и type, например, работают только в Windows, а для Unix-подобных платформ их следует изменить так, чтобы они выполняли команды ls и cat.

Во-вторых, важно помнить, что запуск файлов Python как самостоятельных программ таким способом очень отличается от импорта программных файлов и вызова функций, объявленных в них, и обычно происходит гораздо медленнее. Когда вызываются функции os.system и os.popen, им приходится запускать совершенно новую и независимую программу, выполняемую операционной системой (как правило, они запускают команды в виде новых процессов). При импорте программы в качестве модуля интерпретатор Python просто загружает и выполня-