Перенаправление ввода и вывода с помощью модуля subprocess

perenapravlenie vvoda i vyvoda s pomoshhju modulya subprocess Контекст выполнения сценариев

Еще больший контроль над потоками ввода-вывода порождаемых программ позволяет получить модуль subprocess, представленный в предыдущей главе. Как было показано ранее, с помощью этого модуля можно имитировать функциональность os.popen, но он позволяет добиться большего, например — создавать двунаправленные потоки обмена данными (то есть подключаться к обоим потокам, ввода и вывода, порождаемой программы) и связывать вывод одной программы с вводом другой. Например, этот модуль позволяет множеством способов запускать программу, подключаться к ее стандартному потоку вывода и получать код завершения. Ниже демонстрируются три наиболее типичных способа использования этого модуля для запуска программы и перенаправления ее потока вывода (напомню, что для опробования примеров из этого раздела в Unix-подобных системах вам может потребоваться передавать функции Popen аргумент shell=True, как отмечалось в главе 2):

C:\\PP4E\System\Streams> python

>  >> from subprocess import Popen, PIPE, call

>>> X = call(‘python hello-out.py’) # удобно

Hello shell world

>>> X

0

>>> pipe = Popen(‘python hello-out.py’, stdout=PIPE)

>>> pipe.communicate()[0] # (stdout, stderr)

b’Hello shell world\r\n’

>>> pipe.returncode # код завершения

0

>>> pipe = Popen(‘python hello-out.py’, stdout=PIPE)

>>> pipe.stdout.read()

b’Hello shell world\r\n’

>>> pipe.wait() # код завершения

0

Функция call, использованная в первом из этих трех способов, — это всего лишь функция-обертка, реализованная для удобства (существует несколько таких функций, о которых вы сможете прочитать в руководстве по библиотеке языка Python). Функция communicate делает второй способ немного удобнее третьего (она позволяет отправлять данные в stdin; читать данные из stdout, пока не будет достигнут конец файла; и ожидает завершения дочернего процесса).

Перенаправление и подключение к потоку ввода порождаемой программы реализуется так же просто, хотя и немного сложнее, чем при использовании функции os.popen с флагом режима w’, как было показано в предыдущем разделе (как уже упоминалось в предыдущей главе, в настоящее время функция os.popen реализована с применением инструментов из модуля subprocess, и поэтому сама может считаться функцией-оберткой, реализованной для удобства):

>>> pipe = Popen(‘python hello-in.py’, stdin=PIPE)

>>> pipe.stdin.write(b’Pokey\n’)

6

>>> pipe.stdin.close()

>>> pipe.wait()

0

>>> open(‘hello-in.txt’).read() # вывод был отправлен в файл ‘Hello Pokey\n’

С помощью этого модуля мы можем получить доступ к обоим потокам, ввода и вывода, порожденной программы. Для демонстрации воспользуемся простыми сценариями, выполняющими операции чтения и записи, созданными нами ранее:

C:\\PP4E\System\Streams> type writer.py print(“Help! Help! I’m being repressed!”) print(42)

C:\\PP4E\System\Streams> type reader.py print(‘Got this: “%s”’ % input())

import sys

data = sys.stdin.readline()[:-1]

print(‘The meaning of life is’, data, int(data) * 2)

Следующий программный код демонстрирует возможность чтения и записи в потоки ввода-вывода сценария reader — объект pipe имеет два атрибута с объектами, похожими на файлы, один из которых подключается к потоку ввода, а другой — к потоку вывода (пользователи Python 2.X легко могут узнать в них эквивалент кортежа, возвращаемого функцией os.popen2, ныне исключенной из библиотеки):

>>> pipe = Popen(‘python reader.py’, stdin=PIPE, stdout=PIPE)

>>> pipe.stdin.write(b’Lumberjack\n’)

11

>>> pipe.stdin.write(b’12\n’)

3

>>> pipe.stdin.close()

>>> output = pipe.stdout.read()

>>> pipe.wait() 0

>>> output

b’Got this: “Lumberjack”\r\nThe meaning of life is 12 24\r\n’

Как будет показано в главе 5, при двунаправленном обмене данными с программами, подобными этим, необходимо проявлять осторожность — механизм буферизации потоков вывода может приводить к взаимоблокировке при чередовании операций записи и чтения и, в конечном счете, к необходимости использовать для решения проблемы такие инструменты, как Pexpect (подробнее об этой функции будет рассказываться далее в книге).

Наконец, модуль subprocess позволяет реализовать еще более экзотические формы управления потоками ввода-вывода — ниже демонстрируется соединение двух программ, где поток вывода одного сценария на языке Python подключается к потоку ввода другого. Сначала демонстрируется подключение с использованием командной оболочки, а затем — с помощью модуля subprocess:

C:\\PP4E\System\Streams> python writer.py | python reader.py

Got this: “Help! Help! I’m being repressed!”

The meaning of life is 42 84

C:\\PP4E\System\Streams> python

>>> from subprocess import Popen, PIPE

>>> p1 = Popen(‘python writer.py’, stdout=PIPE)

>>> p2 = Popen(‘python reader.py’, stdin=p1.stdout, stdout=PIPE)

>>> output = p2.communicate()[0]

>>> output

b’Got this: “Help! Help! I\’m being repressed!”\r\nThe meaning of life is 42 84\r\n’

>  >> p2.returncode

0

Нечто похожее можно реализовать с помощью функции os.popen, но тот факт, что она может подключать только один из потоков ввода-вывода (но не оба), препятствует возможности перехватить вывод второго сценария:

>  >> import os

>  >> p1 = os.popen(‘python writer.py’, ‘r’)

>  >> p2 = os.popen(‘python reader.py’, ‘w’)

>  >> p2.write( p1.read() )

36

>  >> X = p2.close()

Got this: “Help! Help! I’m being repressed!”

The meaning of life is 42 84

>  >> print(X)

None

С точки зрения более широкой перспективы, функция os.popen и модуль subprocess являются переносимыми эквивалентами механизма перенаправления потоков ввода-вывода порождаемых программ, реализованного в командных оболочках для Unix-подобных систем. Однако реализации на языке Python с таким же успехом работают в Windows и предоставляют более универсальный способ запуска других программ из сценариев на языке Python. Строки команд, передаваемые им, могут иметь свои особенности в зависимости от платформы (например, в Unix список содержимого каталога можно получить с помощью команды ls, а в Windows — с помощью команды dir), но сами инструменты могут применяться на всех платформах, поддерживающих Python.

Запуск новых, независимых программ и подключение к их потокам ввода-вывода из родительской программы в Unix-подобных системах можно также реализовать с помощью функций os.fork, os.pipe, os.dup и некоторых функций из семейства os.exec. Кроме того, они обеспечивают еще один способ перенаправления потоков ввода-вывода и являются низкоуровневыми эквивалентами таким инструментам, как os.popen (функция os.fork доступна в Windows, в версии Python для Cygwin).

Однако все перечисленные функции являются более сложными инструментами параллельной обработки данных, поэтому мы отложим дальнейшее их обсуждение до главы 5, где дополнительно будет рассказываться об организации каналов и получении кодов завершения. А к модулю subprocess мы вернемся в главе 6, где на его основе реализуем механизм регрессионного тестирования, перехватывающий все три стандартных потока ввода-вывода тестируемого сценария — потоки ввода, вывода и ошибок.

Но перед этим, в главе 4, мы продолжим наше исследование системных интерфейсов, реализованных в библиотеке языка Python, и познакомимся с инструментами для работы с файлами и каталогами. Несмотря на то, что все наше внимание будет сконцентрировано на других вопросах, тем не менее мы увидим, что некоторые инструменты, изученные здесь, могут использоваться, как универсальные инструменты системного программирования. Например, возможность запуска команд оболочки позволяет исследовать содержимое каталогов, а интерфейс объектов файлов, на котором мы подробно остановимся в следующей главе, составляет основу приемов работы с потоками ввода-вывода, обсуждавшихся здесь.

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий