自定义 Popen.communicate 方法给出了错误的输出
Custom Popen.communicate method gives wrong output
让我们从这段代码开始:
proc_stdin.py
import sys
if __name__ == '__main__':
for i, line in enumerate(sys.stdin):
sys.stdout.write(line)
test.py
import subprocess
def run_bad(target, input=None):
proc = subprocess.Popen(
target,
universal_newlines=True,
shell=True,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE if input else subprocess.DEVNULL,
stdout=subprocess.PIPE,
)
if input:
proc.stdin.write(input)
proc.stdin.flush()
proc.stdin.close()
lines = []
for line in iter(proc.stdout.readline, ""):
line = line.rstrip("\n")
lines.append(line)
proc.stdout.close()
ret_code = proc.wait()
return "\n".join(lines)
def run_good(target, input):
return subprocess.Popen(
target,
universal_newlines=True,
shell=True,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
).communicate(input=input)[0]
if __name__ == '__main__':
lst = [
"",
"token1",
"token1\n",
"token1\r\n",
"token1\n\n",
"token1\r\n\ntoken2",
"token1 token2",
"token1\ntoken2",
"token1\r\ntoken2",
"token1\n\ntoken2",
"token1\r\n\ntoken2",
"token1 \ntoken2\ntoken2\n"
]
cmd = "python proc_stdin.py"
for inp in lst:
a, b = run_bad(cmd, inp), run_good(cmd, inp)
if a != b:
print("Error: {} vs {}".format(repr(a), repr(b)))
else:
print("ok: {}".format(repr(a)))
输出:
ok: ''
ok: 'token1'
Error: 'token1' vs 'token1\n'
Error: 'token1\n' vs 'token1\n\n'
Error: 'token1\n' vs 'token1\n\n'
ok: 'token1\n\n\ntoken2'
ok: 'token1 token2'
ok: 'token1\ntoken2'
ok: 'token1\n\ntoken2'
ok: 'token1\n\ntoken2'
ok: 'token1\n\n\ntoken2'
Error: 'token1 \ntoken2\ntoken2' vs 'token1 \ntoken2\ntoken2\n'
我的问题是,为什么 run_bad
和 run_good
的输出在所有情况下都不相等?您将如何更改 run_bad
函数以使输出等于 run_good
?
你也可能想知道,为什么你不直接使用 Popen.communicate 来处理这个特殊情况或子流程模块中的其他助手?好吧,在现实世界中,我正在为 SublimeText3 创建一个插件,这迫使我坚持使用 python3.3(不能使用许多现代子进程好东西)而且我想注入一些回调在从 stdout 读取行时,这是我无法使用 Popen.communicate
方法(据我所知)做的事情。
提前致谢。
如果您从每一行中删除换行符,然后将它们添加回 在 行之间,那么最后一个换行符(如果有)会怎样? (在最后一个换行符之后没有最后一个空行,因为你的 iter
丢弃了它。)这就是为什么 Python 的 readline
(或行迭代)函数 包含 换行符:它们是准确表示文件末尾所必需的。
让我们从这段代码开始:
proc_stdin.py
import sys
if __name__ == '__main__':
for i, line in enumerate(sys.stdin):
sys.stdout.write(line)
test.py
import subprocess
def run_bad(target, input=None):
proc = subprocess.Popen(
target,
universal_newlines=True,
shell=True,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE if input else subprocess.DEVNULL,
stdout=subprocess.PIPE,
)
if input:
proc.stdin.write(input)
proc.stdin.flush()
proc.stdin.close()
lines = []
for line in iter(proc.stdout.readline, ""):
line = line.rstrip("\n")
lines.append(line)
proc.stdout.close()
ret_code = proc.wait()
return "\n".join(lines)
def run_good(target, input):
return subprocess.Popen(
target,
universal_newlines=True,
shell=True,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
).communicate(input=input)[0]
if __name__ == '__main__':
lst = [
"",
"token1",
"token1\n",
"token1\r\n",
"token1\n\n",
"token1\r\n\ntoken2",
"token1 token2",
"token1\ntoken2",
"token1\r\ntoken2",
"token1\n\ntoken2",
"token1\r\n\ntoken2",
"token1 \ntoken2\ntoken2\n"
]
cmd = "python proc_stdin.py"
for inp in lst:
a, b = run_bad(cmd, inp), run_good(cmd, inp)
if a != b:
print("Error: {} vs {}".format(repr(a), repr(b)))
else:
print("ok: {}".format(repr(a)))
输出:
ok: ''
ok: 'token1'
Error: 'token1' vs 'token1\n'
Error: 'token1\n' vs 'token1\n\n'
Error: 'token1\n' vs 'token1\n\n'
ok: 'token1\n\n\ntoken2'
ok: 'token1 token2'
ok: 'token1\ntoken2'
ok: 'token1\n\ntoken2'
ok: 'token1\n\ntoken2'
ok: 'token1\n\n\ntoken2'
Error: 'token1 \ntoken2\ntoken2' vs 'token1 \ntoken2\ntoken2\n'
我的问题是,为什么 run_bad
和 run_good
的输出在所有情况下都不相等?您将如何更改 run_bad
函数以使输出等于 run_good
?
你也可能想知道,为什么你不直接使用 Popen.communicate 来处理这个特殊情况或子流程模块中的其他助手?好吧,在现实世界中,我正在为 SublimeText3 创建一个插件,这迫使我坚持使用 python3.3(不能使用许多现代子进程好东西)而且我想注入一些回调在从 stdout 读取行时,这是我无法使用 Popen.communicate
方法(据我所知)做的事情。
提前致谢。
如果您从每一行中删除换行符,然后将它们添加回 在 行之间,那么最后一个换行符(如果有)会怎样? (在最后一个换行符之后没有最后一个空行,因为你的 iter
丢弃了它。)这就是为什么 Python 的 readline
(或行迭代)函数 包含 换行符:它们是准确表示文件末尾所必需的。