将进程的标准输出重定向到其自己的标准输入

将进程的标准输出重定向到其自己的标准输入

我正在考虑 Haskell 中的自引用列表 - 例如你可能有以下脚本fibonacci.hs

#!/usr/bin/env runghc

fibs :: [Integer]
fibs = 1:1:(zipWith (+) fibs $ tail fibs)

main :: IO ()
main = do
    mapM_ print $ take 50 fibs

因为文件扩展名非常相似(只是一xp点点),所以我推断在 shell 脚本中必须有类似的东西,将进程的标准输出附加到它自己的标准输入。我能想到的就是fibonacci.sh

#!/usr/bin/env sh

{ echo 1; echo 1; } > fibonaccis

tail -f fibonaccis |
    python3 -c 'if True: # dummy to fix indentation
        b = int(input())
        for _ in range(48):
            a, b = b, int(input())
            print(a + b)' >> fibonaccis

cat fibonaccis

然而,我觉得有点不令人满意,因为我必须将所有内容写入文件。显然,这是一个玩具示例,具有更优化的已知算法,但如果我只想生成第 1000 个斐波那契数怎么办?一旦 Python 脚本使用了标准输出,就没有必要保留它们了。

那么有没有更好的方法使用文件来执行此操作,或者理想情况下有一种方法无需文件而仅使用重定向来执行此操作?或者这确实是一个糟糕的主意?这可以通过命名管道来完成吗?

无论我尝试搜索什么,都只是想出将进程的标准输出附加到另一个进程的标准输入的方法,我已经对此有点熟悉了。双向管道似乎也不完全是我想要的。

我知道需要小心以确保进程不会过多地缓冲其 I/O。

答案1

作为马修·冈瑟在回答中指出, A命名管道("fifo") 可用于让脚本读取其自己的输出。

#!/bin/bash

# Create pipe and arrange to remove it when done.
mkfifo mypipe
trap 'rm -f mypipe' EXIT

# Just to start off the sequence. Note that this
# needs to be a background process to avoid
# blocking, as there is nobody reading from the
# pipe yet.
echo '0 1' >mypipe &

# Numbers to produce. Note that the produced numbers
# will overflow at n=93.
n=48

# Read the two numbers from the pipe and put the
# last of these back together with the next number
# in the sequence:
while [ "$n" -gt 0 ] && read a b; do
        c=$(( a + b ))
        printf '%d %d\n' "$b" "$c" >&3

        # Also print the "b" number as output.
        printf '%d\n' "$b"

        n=$(( n - 1 ))
done <mypipe 3>mypipe

我使用额外的文件描述符来写入循环中的管道,只是为了能够像平常一样将生成的数字序列输出到标准输出流(以不超载此目的的标准错误流)。

如果您想通过这种方法使用 Python 代码,您可能会这样做就像是

#!/bin/bash

mkfifo mypipe
trap 'rm -f mypipe' EXIT

printf '%d\n%d\n' 0 1 >mypipe &

python3 -c 'if True: # dummy to fix indentation
    b = int(input())
    for _ in range(48):
        a, b = b, int(input())
        print(a + b)' <mypipe >mypipe

您会注意到,您没有从此脚本获得任何输出,这是因为我们使用 Python 代码的输出作为其自己的输入。为了在终端中获得一些输出,我们将在上面的 shell 循环中执行类似的操作(写入标准输出以外的内容):

#!/bin/bash

mkfifo mypipe
trap 'rm -f mypipe' EXIT

printf '%d\n%d\n' 0 1 >mypipe &

python3 -c '
import sys
b = int(input())
for _ in range(48):
    a, b = b, int(input())
    print(a + b)
    print(b, file=sys.stderr)' <mypipe >mypipe

在这里,我(ab-)使用标准错误流在终端中向用户提供实际结果。如果我像在第一个变体中那样执行 shell 循环(通过新的文件描述符处理管道的输出),那就更整洁了,但我对 Python 的了解严重缺乏。

相关内容