Разница между каналами bash и python

У меня есть следующие три скрипта Python:

parent1.py

import subprocess, os, sys

relpath = os.path.dirname(sys.argv[0])
path = os.path.abspath(relpath)
child = subprocess.Popen([os.path.join(path, 'child.lisp')], stdout = subprocess.PIPE)
sys.stdin = child.stdout
inp = sys.stdin.read()
print(inp.decode())

родитель2.py:

import sys
inp = sys.stdin
print(inp)

ребенок.ру:

print("This text was created in child.py")

Если я вызову parent1.py с помощью:

python3 parent1.py

это дает мне, как и ожидалось, следующий результат:

This text was created with child.py

если я вызову parent2.py с помощью:

python3 child.py | python3 parent2.py

я получаю тот же результат. Но в первом примере я получаю вывод child.py в виде байтов, а во втором — непосредственно в виде строки. Почему это? Это просто разница между трубами python и bash или есть что-то, что я мог бы сделать иначе, чтобы избежать этого?


person Kritzefitz    schedule 08.05.2013    source источник
comment
попробуйте это   -  person scott    schedule 08.05.2013


Ответы (1)


Когда python открывает stdin и stdout, он определяет, какую кодировку использовать, и использует текст I. /O, чтобы дать вам строки Unicode.

Но subprocess не определяет (и не может) кодировку запускаемого вами подпроцесса, поэтому он будет возвращать байты. Вы можете использовать io.TextIOWrapper() instance, чтобы обернуть канал child.stdout для предоставления юникод данные:

sys.stdin = io.TextIOWrapper(child.stdout, encoding='utf8')
person Martijn Pieters    schedule 08.05.2013
comment
Ага. Я хотел бы добавить, что в ОС есть только один вид каналов, который одинаково используется bash и Python. Интерпретация потока может быть разной, и Python различает два случая; в одном он интерпретирует ввод как байты, в другом - как строку/юникод. - person Alfe; 08.05.2013
comment
Спасибо, это сработало. Если я сейчас хочу сделать что-то вроде 'cat /bin/bash | parent2.py» вызывает ошибку UnicodeDecodeError, поскольку sys.stdin.read() не возвращает байты. Есть ли способ обойти это? - person Kritzefitz; 08.05.2013
comment
@Alfe: Ну, он по-прежнему интерпретирует ввод как байты в обоих случаях, он просто автоматически оборачивает поток в TextIOWrapper для вас в последнем случае. В любом случае вы можете получить базовый поток байтов или вручную прикрепить свою собственную оболочку. Но все же полезный момент. - person abarnert; 08.05.2013
comment
@IchUndNichtDu: sys.stdin — это обычный TextIOWrapper, как и все остальное. (Попробуйте напечатать его repr.) Таким образом, вы можете получить байты внутри него всеми обычными способами. Например, вы можете получить его fileno() и os.read() из него (но не путайте чтение как из оболочки, так и из файла no!). - person abarnert; 08.05.2013
comment
@IchUndNichtDu: вы можете попробовать проверить, имеет ли объект атрибут encoding; если нет, то нужно завернуть; if not hasattr(fileobj, 'encoding'): fileobj = io.TextIOWrapper(fileobj, encoding=...). - person Martijn Pieters; 08.05.2013