> **Источник:** https://python-all.ru/2.7/library/popen2.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 17.5. [`popen2`](https://python-all.ru/2.7/library/popen2.html#module-popen2) – Подпроцессы с доступными потоками ввода-вывода

Устарело с версии 2.6: Этот модуль устарел. Используйте модуль [`subprocess`](https://python-all.ru/2.7/library/subprocess.html#module-subprocess). Обратите особое внимание на раздел [Замена старых функций модулем subprocess](https://python-all.ru/2.7/library/subprocess.html#subprocess-replacements).

Этот модуль позволяет запускать процессы и подключаться к их каналам ввода/вывода/ошибок, а также получать коды возврата в Unix и Windows.

Модуль [`subprocess`](https://python-all.ru/2.7/library/subprocess.html#module-subprocess) предоставляет более мощные средства для порождения новых процессов и получения их результатов. Использование модуля [`subprocess`](https://python-all.ru/2.7/library/subprocess.html#module-subprocess) предпочтительнее использования модуля [`popen2`](https://python-all.ru/2.7/library/popen2.html#module-popen2).

Основной интерфейс, предоставляемый этим модулем, – это три порождающие функции. Для каждой из них, если указан *bufsize*, он задает размер буфера для каналов ввода-вывода. *mode*, если указан, должен быть строкой `'b'` или `'t'`; в Windows это необходимо для определения, должны ли файловые объекты открываться в двоичном или текстовом режиме. Значение по умолчанию для *mode* – `'t'`.

В Unix *cmd* может быть последовательностью, и в этом случае аргументы будут переданы напрямую программе без участия оболочки (как с [`os.spawnv()`](https://python-all.ru/2.7/library/os.html#os.spawnv)). Если *cmd* – строка, она будет передана оболочке (как с [`os.system()`](https://python-all.ru/2.7/library/os.html#os.system)).

Единственный способ получить коды возврата дочерних процессов – использовать методы `poll()` или `wait()` классов [`Popen3`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen3) и [`Popen4`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen4); они доступны только в Unix. Эта информация недоступна при использовании функций [`popen2()`](https://python-all.ru/2.7/library/popen2.html#module-popen2), [`popen3()`](https://python-all.ru/2.7/library/popen2.html#popen2.popen3) и [`popen4()`](https://python-all.ru/2.7/library/popen2.html#popen2.popen4) или эквивалентных функций из модуля [`os`](https://python-all.ru/2.7/library/os.html#module-os). (Обратите внимание, что кортежи, возвращаемые функциями модуля [`os`](https://python-all.ru/2.7/library/os.html#module-os), находятся в другом порядке, чем те, что возвращаются модулем [`popen2`](https://python-all.ru/2.7/library/popen2.html#module-popen2).)

#### `popen2.popen2(cmd[, bufsize[, mode]])`

Выполняет *cmd* как подпроцесс. Возвращает файловые объекты `(child_stdout, child_stdin)`.

#### `popen2.popen3(cmd[, bufsize[, mode]])`

Выполняет *cmd* как подпроцесс. Возвращает файловые объекты `(child_stdout, child_stdin, child_stderr)`.

#### `popen2.popen4(cmd[, bufsize[, mode]])`

Выполняет *cmd* как подпроцесс. Возвращает файловые объекты `(child_stdout_and_stderr, child_stdin)`.

Новое в версии 2.0.

В Unix также доступен класс, определяющий объекты, возвращаемые порождающими функциями. Они не используются в реализации для Windows и недоступны на этой платформе.

#### `class popen2.Popen3(cmd[, capturestderr[, bufsize]])`

Этот класс представляет дочерний процесс. Обычно экземпляры [`Popen3`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen3) создаются с помощью порождающих функций [`popen2()`](https://python-all.ru/2.7/library/popen2.html#module-popen2) и [`popen3()`](https://python-all.ru/2.7/library/popen2.html#popen2.popen3), описанных выше.

Если для создания объектов [`Popen3`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen3) не используются вспомогательные функции, параметр *cmd* – это команда оболочки для выполнения в подпроцессе. Флаг *capturestderr*, если true, указывает, что объект должен захватывать стандартный вывод ошибок дочернего процесса. По умолчанию false. Если указан параметр *bufsize*, он задает размер буферов ввода-вывода для дочернего процесса.

#### `class popen2.Popen4(cmd[, bufsize])`

Аналогично [`Popen3`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen3), но всегда захватывает стандартную ошибку в тот же файловый объект, что и стандартный вывод. Обычно создаются с помощью [`popen4()`](https://python-all.ru/2.7/library/popen2.html#popen2.popen4).

Новое в версии 2.0.

## 17.5.1. Объекты Popen3 и Popen4

Экземпляры классов [`Popen3`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen3) и [`Popen4`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen4) имеют следующие методы:

#### `Popen3.poll()`

Возвращает `-1`, если дочерний процесс еще не завершился, или его код состояния (см. [`wait()`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen3.wait)) в противном случае.

#### `Popen3.wait()`

Ожидает завершения дочернего процесса и возвращает его код состояния. Код состояния содержит как код возврата процесса, так и информацию о том, завершился ли он с помощью системного вызова `exit()` или был завершен сигналом. Функции для интерпретации кода состояния определены в модуле [`os`](https://python-all.ru/2.7/library/os.html#module-os); см. раздел [Управление процессами](https://python-all.ru/2.7/library/os.html#os-process) для семейства функций `W*()`.

Также доступны следующие атрибуты:

#### `Popen3.fromchild`

Файловый объект, предоставляющий вывод от дочернего процесса. Для экземпляров [`Popen4`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen4) он будет предоставлять как стандартный вывод, так и стандартный поток ошибок.

#### `Popen3.tochild`

Файловый объект, предоставляющий ввод в дочерний процесс.

#### `Popen3.childerr`

Файловый объект, предоставляющий вывод ошибок от дочернего процесса, если для конструктора *capturestderr* было true, в противном случае `None`. Для экземпляров [`Popen4`](https://python-all.ru/2.7/library/popen2.html#popen2.Popen4) это всегда будет `None`.

#### `Popen3.pid`

PID дочернего процесса.

## 17.5.2. Проблемы управления потоком

При работе с любой формой межпроцессного взаимодействия необходимо тщательно продумывать управление потоком. Это относится и к файловым объектам, предоставляемым этим модулем (или эквивалентами из модуля [`os`](https://python-all.ru/2.7/library/os.html#module-os)).

При чтении вывода из дочернего процесса, который записывает много данных в стандартную ошибку, в то время как родитель читает из стандартного вывода дочернего процесса, может возникнуть взаимоблокировка. Аналогичная ситуация может возникнуть и при других комбинациях чтения и записи. Основные факторы: один процесс записывает более `_PC_PIPE_BUF` байт в блокирующем режиме, в то время как другой процесс читает из первого, также в блокирующем режиме.

Есть несколько способов справиться с этой ситуацией.

Самое простое изменение приложения во многих случаях – следовать этой модели в родительском процессе:

```python
import popen2

r, w, e = popen2.popen3('python slave.py')
e.readlines()
r.readlines()
r.close()
e.close()
w.close()
```

с таким кодом в дочернем процессе:

```python
import os
import sys

# обратите внимание, что каждый из этих операторов print
# записывает одну длинную строку

print >>sys.stderr, 400 * 'this is a test\n'
os.close(sys.stderr.fileno())
print >>sys.stdout, 400 * 'this is another test\n'
```

Учитывайте, что `sys.stderr` необходимо закрыть после записи всех данных, иначе `readlines()` не вернёт управление. Также обратите внимание, что следует использовать [`os.close()`](https://python-all.ru/2.7/library/os.html#os.close), поскольку `sys.stderr.close()` не закроет `stderr` (в противном случае присваивание `sys.stderr` закроет его без предупреждения, и дальнейшие ошибки не будут выведены).

Приложениям, которым требуется более общий подход, следует интегрировать ввод-вывод через каналы в свои [`select()`](https://python-all.ru/2.7/library/select.html#module-select) циклы или использовать отдельные потоки для чтения каждого из файлов, предоставленных используемой `popen*()` функцией или `Popen*` классом.

> **См. также**
>
> **Модуль [`subprocess`](https://python-all.ru/2.7/library/subprocess.html#module-subprocess)**
>
> Модуль для порождения и управления подпроцессами.
