如何从标准输入中读取一行,并将其余部分传递给子进程?



如果您从sys.stdin中获取readline(),则将其余部分传递给子进程似乎不起作用。

import subprocess
import sys
header = sys.stdin.buffer.readline()
print(header)
subprocess.run(['nl'], check=True)

(我使用sys.stdin.buffer来避免任何编码问题;这个句柄返回原始字节。)

这个运行了,但是我没有从子进程得到任何输出;

bash$ printf '%sn' foo bar baz | python demo1.py
b'foon'

如果我取出readline等,子进程读取标准输入并产生我期望的输出。

bash$ printf '%sn' foo bar baz |
> python -c 'import subprocess; subprocess.run(["nl"], check=True)'
1  foo
2  bar
3  baz

当我开始读取stdin时,Python是否在缓冲其余的stdin,或者这里发生了什么?使用python -u运行并不能消除这个问题(事实上,它的文档只提到它改变了stdoutstderr的行为)。但如果我传入更大的数据量,我确实得到了一些:

bash$ wc -l /etc/services
13921 /etc/services
bash$ python demo1.py </etc/services  | head -n 3
1     27/tcp     # NSW User System FE
2  #                          Robert Thomas <BThomas@F.BBN.COM>
3  #                28/tcp    Unassigned
(... traceback from broken pipe elided ...)
bash$  fgrep -n 'NSW User System FE' /etc/services 
91:nsw-fe           27/udp     # NSW User System FE
92:nsw-fe           27/tcp     # NSW User System FE
bash$ sed -n '1,/NSW User System FE/p' /etc/services | wc
91     449    4082

(所以,看起来它从一开始就吃了4096字节。)

有什么方法可以避免这种行为吗?我想只从开头读取一行,并将其余的传递给子进程。

在循环中重复调用sys.stdin.buffer.readline(-1)没有帮助。

这实际上是Read line from shell pipe, pass to exec, and keep to variable的后续,但我想关注的是,对我来说,这个问题中令人惊讶的方面。

这是因为sys.stdin是在默认缓冲模式下使用内置open函数创建的,它使用大小为io.DEFAULT_BUFFER_SIZE的缓冲区,在大多数系统上是40968192字节。

为了使父进程从标准输入中精确地使用一行文本,因此可以通过将0作为buffering参数传递给openos.fdopen函数,从而在禁用缓冲区的情况下打开它:

# subp1.py
import os
import sys
import subprocess
# or with the platform-dependent device file:
# unbuffered_stdin = open('/dev/stdin', 'rb', buffering=0)
unbuffered_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)
print(unbuffered_stdin.readline())
subprocess.run(['nl'], check=True)

:

printf "foonbarn" | python subp1.py

将输出:

b'foon'
1  bar

相关内容

  • 没有找到相关文章

最新更新