>>> pipe.read()
‘Hello shell world\n’
>>> print(pipe.close()) # код завершения: None - успех
None
Но сценарии на языке Python могут играть роль источников данных, подаваемых в поток стандартного ввода порождаемых программ, -если передать функции os.popen флаг режима «w» вместо «г», подразумеваемого по умолчанию, она вернет объект, подключенный к потоку ввода порожденной программы. Все, что мы запишем в этот объект со стороны родительского сценария, окажется в стандартном потоке ввода запущенной программы:
>>> pipe = os.popen('python hello-in.py', 'w') # ‘w’- запись в stdin программы
>>> pipe.write('Gumby\n')
6
>>> pipe.close() # символ \n в конце необязателен
>>> open('hello-in.txt').read() # вывод был отправлен в файл
‘Hello Gumby\n’
Функция popen достаточно сообразительна, чтобы выполнить команду оболочки как независимый процесс на платформах, поддерживающих такую возможность. Она принимает необязательный третий параметр, который используется для управления буферизацией записываемого текста, но мы пока не будем затрагивать эту тему.
Перенаправление ввода и вывода с помощью модуля 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