Python 如何正确地将stdin重定向到按顺序创建的多个子进程? 上下文

Python 如何正确地将stdin重定向到按顺序创建的多个子进程? 上下文,python,Python,我正在试验一个类似于vegeta的脚本。在这个脚本中,我使用subprocess.run()顺序运行多个子流程,并期望脚本的标准输入在其整个生命周期内(每个5秒钟)重定向到这些子流程 我按照如下方式调用脚本,通过管道将无限量的输入传递给它,每个输入由一行新行分隔vegeta连续读取此输入,直到--持续时间结束: $ target-generator | ./ramp-requests.py 问题 第一个子进程(rate=1.0)似乎像我预期的那样接收stdin,并且每次命令都成功运行 但是,第

我正在试验一个类似于vegeta的脚本。在这个脚本中,我使用
subprocess.run()
顺序运行多个子流程,并期望脚本的标准输入在其整个生命周期内(每个5秒钟)重定向到这些子流程

我按照如下方式调用脚本,通过管道将无限量的输入传递给它,每个输入由一行新行分隔
vegeta
连续读取此输入,直到
--持续时间
结束:

$ target-generator | ./ramp-requests.py
问题 第一个子进程(rate=1.0)似乎像我预期的那样接收stdin,并且每次命令都成功运行

但是,第二次迭代(rate=2.0)与所有后续迭代一起以静默方式失败。如果我使用
vegeta report
命令检查相应的报告文件(例如
results\u 2000.bin
),我会看到一些错误片段,例如
parse error:syntax error靠近'ource:[“c..
的偏移量0


我的直觉告诉我,第二个子过程开始消耗第一个离开的输入,在中间行,但是在<代码>子进程.Run()>代码>之后注入<代码> sys .STDIN .Read Load()/代码>。如果是这样的话,我怎样才能干净地解决这个问题,并确保每个子进程都从A开始读取。“好”位置?

在父进程中读取stdin中的许多行,并将其作为-its-stdin传递给子进程。根据需要重复。这样,您就不必担心子进程将stdin弄得一团糟

你可以随意借用别人的想法

HTH

正如在的注释中提到的,Python 3以缓冲文本模式打开stdin,因此
sys.stdin.read(1)
sys.stdin.readline()
都会导致预读,并且不会将
sys.stdin
流重新定位到新行的开头

但是,有一种方法可以通过在二进制模式下打开
sys.stdin
来禁用缓冲,正如在回答以下问题时所指出的:

unbuffered\u stdin=os.fdopen(sys.stdin.fileno(),'rb',buffering=0)
通过这样做,可以在每个子流程返回后,从该无缓冲io对象读取截断的输入,直到行尾:

#运行vegeta攻击
以差饷计算的差饷:
# [...]
cmd='vegeta攻击[…]'
subprocess.run(cmd,shell=True,encoding='utf-8')
#读取可能被截断的输入,直到下一个“\n”字节
#将stdin重新定位到安全消费的位置。
无缓冲标准读入线()
打印读取行显示与以下输出类似的内容:

b'a4b-b142-fabe0e96a6ca"],"Ce-Type":["perf.drill"],"Ce-Source":["load-test"]}}\n'
所有子流程现在都已成功执行:

$ for r in results_*.bin; do vegeta report "$r"; done
[...]
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:5
Error Set:
[...]
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:7
Error Set:
[...]
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:8
Error Set:
[...]


另请参见(Python 3文档)

如果子进程读取stdin直到EOF,那么管道中就没有任何内容可供后续进程读取。此外,许多程序都使用缓冲输入。因此,第一个子进程可能会缓冲不使用的管道中的输入。下一个子进程将无法读取。我认为没有好的解决方案。这是bufferinG输入,因此它在管道中向前读,下一个调用在一行中间开始。Python也使用缓冲输入。问题是操作系统一次不提供任何方式读取一行,除非从终端读取。因此,除非应用程序一次读取一个字符,这是非常低效的,它将提前读取。我唯一关心的是,当速率接近100k请求/秒时,我必须缓冲的行数变得非常高。在本例中,每次“攻击”仅持续5秒,但实际上它们可以持续60秒(因此我可以评估自动校准是否以特定速率等发生)。
$ for r in results_*.bin; do vegeta report "$r"; done
[...]
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:5
Error Set:
[...]
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:7
Error Set:
[...]
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:8
Error Set:
[...]