Bash和Python管道的区别

5

我有以下三个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())

parent2.py:

import sys
inp = sys.stdin
print(inp)

child.py:

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管道之间的区别,还是我可以采取其他措施来避免这种情况?

请尝试此链接:https://dev59.com/21HTa4cB1Zd3GeqPQlZ7?answertab=votes#tab-top - scott
1个回答

3
当Python打开stdinstdout时,它会检测要使用的编码,并使用text I/O来提供Unicode字符串。
但是subprocess无法检测启动的子进程的编码,因此它将返回字节。您可以使用io.TextIOWrapper()实例来包装child.stdout管道以提供Unicode数据:
sys.stdin = io.TextIOWrapper(child.stdout, encoding='utf8')

2
是的。我想补充一点,操作系统中只有一种管道,同时被 Bash 和 Python 使用。流的解释可能不同,Python 区分这两种情况:在一种情况下,它将输入解释为字节,在另一种情况下则解释为字符串/Unicode。 - Alfe
谢谢,那个方法可行。如果我现在想做类似于“cat /bin/bash | parent2.py”的操作,会出现UnicodeDecodeError错误,因为sys.stdin.read()不返回字节。有没有办法解决这个问题? - Kritzefitz
1
@Alfe:在这两种情况下,它仍然将输入解释为字节,只是在后一种情况下自动为您包装了一个“TextIOWrapper”流。您可以访问底层字节流或手动附加自己的包装器,在任何一种情况下都可以。但仍然是一个有用的观点。 - abarnert
@IchUndNichtDu:sys.stdin只是一个普通的TextIOWrapper,就像其他任何东西一样。(尝试打印它的repr。)因此,您可以以所有常规方式访问其中的字节。例如,您可以从中获取其fileno()os.read()(但不要混淆来自包装器和文件描述符的读取!)。 - abarnert
@IchUndNichtDu:你可以尝试查看对象是否具有“编码”属性;如果没有,你需要进行包装;if not hasattr(fileobj, 'encoding'): fileobj = io.TextIOWrapper(fileobj, encoding=...) - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接