如何在不等待第一个命令完成的情况下进行管道传输?

Mat*_*irk 3 powershell

我正在尝试将输出日志通过管道传输到另一个脚本进行处理。但是,如果我正确理解这个问题,powershell 会等待第一个命令完成,然后再将输出发送到第二个命令。

例如,这按预期工作(脚本收到“hi”),因为echo很快完成:

echo "hi" | python script.py
Run Code Online (Sandbox Code Playgroud)

而在这个(caddy是网络服务器,所以它没有完成)中,球童的标准输出永远不会到达脚本的标准输入:

caddy run | python script.py
Run Code Online (Sandbox Code Playgroud)

是否可以将第一个命令的输出异步转发到第二个命令,而无需等待第一个命令完成?

--

编辑:最小的例子

echo "hi" | python script.py
Run Code Online (Sandbox Code Playgroud)
caddy run | python script.py
Run Code Online (Sandbox Code Playgroud)
# output.py

from time import sleep

while True:
    print("SENDING")
    sleep(1)
Run Code Online (Sandbox Code Playgroud)

这有效(打印到 stdout Received: SENDING):

python onetime.py | python input.py
Run Code Online (Sandbox Code Playgroud)

这不起作用(没有打印任何内容):

python output.py | python input.py
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

的确:

  • Windows PowerShell中,当管道传输到外部程序时,输入命令的输出是意外的

    • 首先在内存中完整收集

    • 并且仅在输入命令退出后,收集的输出才会通过管道传递。

  • 幸运的是,这个问题已在PowerShell (Core) 7+中得到修复,输入命令的输出现在往常一样,在行可用时逐行进行流式传输。(如果接收命令是PowerShell命令,这也是它在 Windows PowerShell 中的工作方式。)

一个简单的例子

  • 请注意,提供输入的命令是 PowerShell 命令还是外部程序并不重要- 重要的是接收命令是外部程序

  • 示例输入命令输出 3 个数字,一次一个,每个数字后等待 1 秒,最后等待用户按下Enter才退出。
    接收命令findstr.exe .简单地回显每个数字。

# In Windows PowerShell:
#   3 seconds elapse without anything getting printed, because PowerShell
#   is collecting the output first.
#   Then the user is prompted to press Enter.
#   Only if and when the user does so do the numbers print.
# In PowerShell 7+:
#   The numbers print as they are being emitted by the ForEach-Object call, and
#   only then is the user prompted to press Enter.
1..3 | ForEach-Object { $_; sleep 1 } -end { pause } | findstr.exe .
Run Code Online (Sandbox Code Playgroud)

解决方法

cmd.exe通过的 CLI调用( cmd /c),因为cmd.exe的管道行为符合预期(在类Unix平台上,类似地使用sh -c):

cmd /c 'caddy run | python script.py'
Run Code Online (Sandbox Code Playgroud)

实际上:

  • cmd.exe的 ( /bin/sh's) 管道是一个原始字节管道,与 PowerShell 的管道不同,从 v7.3.4 开始“只讲文本”

  • 因此,在需要将原始字节数据发送到/外部程序之间/发送到文件的情况下,也需要上述解决方法 - 请参阅此答案