#python #subprocess
#python #подпроцесс
Вопрос:
Я обнаружил следующее поведение довольно запутанным:
Python 3.7.2 (default, Feb 12 2019, 08:15:36)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> import sys
>>> sys.stdout.write(b'')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: write() argument must be str, not bytes
>>> subprocess.run('echo', stdout=sys.stdout)
CompletedProcess(args='echo', returncode=0)
sys.stdout
не принимает двоичный файл. Я не указывал кодировку в своем subprocess
вызове, это означает, что она должна быть потоковой двоичной. Как подпроцесс узнал, что в этом случае не нужно передавать двоичные данные файлового объекта?
Ответ №1:
Вы неправильно понимаете, как работают подпроцессы. Подпроцесс не взаимодействует с sys.stdout
объектом — этот объект существует только в python и только в вашем процессе.
Чтобы понять, что происходит на самом деле, вам сначала нужно знать, как ОС обрабатывает ввод-вывод. На уровне операционной системы каждому открытому файлу (или каналу) присваивается идентификатор — это называется дескриптором файла. Например, дескриптором для stdout
обычно является число 1
:
>>> sys.stdout.fileno()
1
Когда вы запускаете подпроцесс, в подпроцесс передается только этот дескриптор файла. Подпроцесс не имеет доступа к sys.stdout
файловому объекту. Все, что может сделать подпроцесс, это записать байты в полученный дескриптор файла. (На уровне ОС есть только байты, текста нет.) Вы не можете заставить подпроцесс использовать определенную кодировку.
Когда вы передаете encoding
аргумент subprocess.run
, эта кодировка используется только для кодирования текста, который вы отправляете в подпроцесс или получаете от подпроцесса. Это не влияет на сам подпроцесс, это влияет только на то, как ваш процесс взаимодействует с подпроцессом.
Комментарии:
1. Фактически, вы не можете передать какой-либо файловый объект, не основанный на файловых дескрипторах, в подпроцесс; например,
subprocess.run('echo', stdout=io.BytesIO())
произойдет сбой сio.UnsupportedOperation: fileno
2. Еще одним интересным следствием этого факта является то, что буфер передаваемого вами файлового объекта неявно очищается. Поэтому, если у вас есть
print("A", end="", file=sys.stdout); subprocess.run (("echo", "B"), stdout=sys.stdout)
, A не будет сброшен до запуска подпроцесса.