Какая разница между subprocess.call() и subprocess.Popen() делает PIPE менее безопасным для первого?
Я посмотрел документацию для них обоих.
Этот вопрос вызван комментарием J.F. здесь: Получение вывода subprocess.call()
В текущей документации Python для subprocess.call()
говорится следующее об использовании PIPE
для subprocess.call()
:
Примечание. Не используйте stdout=PIPE
или stderr=PIPE
с помощью этой функции. Детский процесс будет блокироваться, если он генерирует достаточный вывод в канал для заполнения буфера для буфера OS, поскольку каналы не читаются.
Python 2.7 subprocess.call()
:
Примечание. Не используйте stdout=PIPE
или stderr=PIPE
с этой функцией, так как это может быть блокировка на основе выходного тома дочернего процесса. Используйте Popen с методом communication(), когда вам нужны трубы.
Python 2.6 не содержит таких предупреждений.
Кроме того, subprocess.call()
и subprocess.check_call()
, похоже, не имеют доступа к их выходным данным, за исключением использования stdout = PIPE с сообщением():
https://docs.python.org/2.6/library/subprocess.html#convenience-functions
Обратите внимание, что если вы хотите отправить данные процессам stdin
, вам нужно создать объект Popen
с помощью stdin=PIPE
. Точно так же, чтобы получить ничего, кроме None в кортеже результата, вам нужно также дать stdout=PIPE
и/или stderr=PIPE
.
https://docs.python.org/2.6/library/subprocess.html#subprocess.Popen.communicate
Какая разница между subprocess.call()
и subprocess.Popen()
делает PIPE
менее безопасным для subprocess.call()
?
Конкретнее: Зачем нужен тупик subprocess.call()
на основе выходного тома дочернего процесса. ", а не Popen()
?
Ответы
Ответ 1
call()
- это просто Popen().wait()
(± обработка ошибок).
Вы не должны использовать stdout=PIPE
с call()
, потому что он не читает из канала, и поэтому дочерний процесс зависает, как только он заполняет соответствующий буфер буфера операционной системы. Вот изображение, показывающее, как данные передаются в конвейере command1 | command2
:
![pipe/stdio buffers]()
Не имеет значения, какова ваша версия Python - буфер канала (посмотрите на изображение) находится вне вашего процесса Python. Python 3 не использует C stdio, но влияет только на внутреннюю буферизацию. Когда внутренний буфер сбрасывается, данные поступают в трубу. Если command2
(ваша родительская программа Python) не читается из канала, тогда command1
(например, дочерний процесс, начинающийся с call()
) будет зависать, как только буфер буфера заполнен (pipe_size = fcntl(p.stdout, F_GETPIPE_SZ)
~ 65K в моем ящике Linux (максимальное значение /proc/sys/fs/pipe-max-size
~ 1M)).
Вы можете использовать stdout=PIPE
, если вы прочтете из канала позже, например, используя метод Popen.communicate()
. Вы также можете читать с process.stdout
(файловый объект, представляющий канал).
Ответ 2
Оба call
и Popen
предоставляют средства для доступа к выходу вашей команды:
- С помощью
Popen
вы можете использовать communicate
или предоставить файловый дескриптор или файл-объект параметру stdout=...
.
- С
call
ваш единственный вариант - передать файловый дескриптор или файл-объект в параметр stdout=...
(вы не можете использовать communicate
с этим).
Теперь причина, по которой stdout=PIPE
является небезопасной при использовании с call
, заключается в том, что call
не возвращается до завершения подпроцесса, это означает, что весь вывод должен был находиться в памяти до этого момента и если объем вывода слишком большой, то это будет заполнять буфер труб OS.
Ссылки, в которых вы можете проверить приведенную выше информацию, следующие:
- В соответствии с this параметры для
call
и Popen
совпадают:
Приведенные выше аргументы являются лишь наиболее распространенными из них ниже в часто используемых аргументах (следовательно, слегка нечетные обозначения в сокращенная подпись). Полная подпись функции такая же, как и функции конструктора Popen - эти функции передают все поставляемые аргументы непосредственно к этому интерфейсу.
- В соответствии с this возможные значения для параметра
stdout
:
Допустимыми значениями являются PIPE, существующий файловый дескриптор (положительный целое), существующий файловый объект и None. PIPE указывает, что новый должен быть создан канал для ребенка