【问题标题】:Read stdin in chunks in Bash pipe在 Bash 管道中分块读取标准输入
【发布时间】:2023-11-20 12:33:02
【问题描述】:

我有一些 shell 脚本可以与这样的管道一起使用:

foo.sh | bar.sh

我的bar.sh 调用了一些命令行程序,该程序只能采用一定数量的标准输入。因此,我希望将foo.sh 的大标准输出分成N 行以进行多个bar.sh 调用。本质上,对foo.sh 的标准输出进行分页并执行多个bar.sh

有可能吗?我希望像foo.sh | ??? | bar.sh 这样的管道之间有一些魔力。 xargs -n 并没有完全得到我想要的。

【问题讨论】:

  • program that can only take a certain number of lines of stdin 你想对管道中剩余的stdin 做什么?丢弃它?如果是,您可以使用head 命令。
  • @anishsane 他想用成批的线路反复呼叫bar.sh

标签: linux bash pipe


【解决方案1】:

我离测试这一点的机器还差得很远,但你需要GNU Parallel 来简化这个过程 - 大致如下:

foo.sh | parallel --pipe -N 10000 -k bar.sh

作为额外的奖励,它可以并行运行与 CPU 内核一样多的 bar.sh

如果您一次只需要一个bar.sh,请添加-j 1

如果你想看看它会做什么但不做任何事情,请添加--dry-run

【讨论】:

    【解决方案2】:

    使用while read 循环。

    foo.sh | while read line1 && read line2 && read line3; do
        printf "%s\n%s\n%s\n" "$line1" "$line2" "$line3" | bar.sh
    done
    

    对于较大的 N,编写一个循环的函数。

    read_n_lines() {
        read -r line || return 1
        echo "$line"
        n=$(($1 - 1))
        while [[ $n -gt 0 ]] && read -r line; do
            echo "$line"
            n=$((n-1))
        done
    }
    

    那么你可以这样做:

    n=20
    foo.sh | while lines=$(read_n_lines $n); do
        printf "%s\n" "$lines" | bar.sh
    done
    

    【讨论】:

    • 这很接近,但我的 N 是一个很大的数,虽然用 N 个谓词编写 while 条件是可行的,但我宁愿不这样做。是否可以不必编写 N 个谓词?
    • @StephenChu 我添加了一个函数,一次读取和回显 N 行。