【问题标题】:Run pipe for each item in bash array为 bash 数组中的每个项目运行管道
【发布时间】:2019-07-16 11:20:36
【问题描述】:

假设我有一个数组:

ITEMS=(
  "foo"
  "bar"
  "baz"
)

我想运行以下命令:

cat template \
  | my_command $ITEMS[1] \
  | my_command $ITEMS[2] \
  | my_command $ITEMS[3] \
  > output

我不想为每个数组项硬编码my_command,因为数组是动态的。

我可以做到以下几点:

cat template > output
for var in "${ITEMS[@]}"; do
  cat output | my_template $var > output
done

但这似乎很笨拙,因为它多次写入文件。

当调用次数未知时,有没有办法将所有 my_command 调用放入同一个命令中?

【问题讨论】:

  • my_command 是否只接受一个输入参数?或者你可以重写my_command 来接受多个值吗?
  • my_command 是我没有编写的外部程序,所以假设它必须采用提供的格式。

标签: arrays bash loops pipe


【解决方案1】:

使用一些好的老式递归。

  • 如果只有一个参数(1) 情况)直接运行命令。
  • 如果有多个(*) 案例)使用第一个参数 (${args[0]}) 运行它,并将其传递给 multipipe 的递归调用,并删除第一个参数 (${args[@]:1})。李>
multipipe() {
    local cmd=$1
    local args=("${@:2}")

    case ${#args[@]} in
        0) ;;
        1) "$cmd" "${args[0]}";;
        *) "$cmd" "${args[0]}" | multipipe "$cmd" "${args[@]:1}";;
    esac
}

multipipe my_command "${ITEMS[@]}" <template >output

【讨论】:

  • 这是最清晰的解决方案,不依赖于eval。 +1
【解决方案2】:

这是一种使用printf 和备受诟病的eval 的方法:

eval $(printf 'cat template '; printf '| my_command "%s" ' "${ITEMS[@]}") > output

仅当您确定ITEMS 数组中没有恶意条目时才使用它。

【讨论】:

    【解决方案3】:

    你可以将整个循环块重定向到输出,所以:

    for var in "${ITEMS[@]}"; do
      my_template "${var}" <template
    done >output
    

    或者,如果您的 my_template 程序可以接受多个项目作为参数,您可以在一次调用中将所有数组传递给它:

    my_template "${ITEMS[@]}" <template
    

    这将调用:

    my_template "foo" "bar" "baz" <template
    

    【讨论】:

    • 我喜欢第一种方法,我认为它适用于我的情况,但如果(假设)my_command 的每次调用都依赖于先前的输出,它会起作用吗?
    • 不,每个调用都独立于其他进程处理template
    • @FelaMaslen 您可以使用命名管道在循环中链接输入和输出:mypipe=$(mktemp -u); mkfifo -m 600 "${mypipe}"; for var in "${ITEMS[@]}"; do my_template "${var}" &lt;"${mypipe}" &gt;&gt;"${mypipe}"; done; cat "${mypipe}" &gt;output; rm "${mypipe}"
    猜你喜欢
    • 1970-01-01
    • 2017-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-10
    • 2018-11-01
    • 2012-12-17
    • 1970-01-01
    相关资源
    最近更新 更多