流程替换的惯用用法

Idiomatic use of process substitution

我从 Bash 的手册页中学习了 Bash 进程替换。不幸的是,我对该功能的不熟练使用很丑陋。

DEV=<(some commands that produce lines of data) && {
    while read -u ${DEV##*/} FIELD1 FIELD2 FIELD3; do
        some commands that consume the fields of a single line of data
    done
}

熟练的程序员有其他方法可以做到这一点吗?

如果需要可执行样本,试试这个:

DEV=<(echo -ne "Cincinnati Hamilton Ohio\nAtlanta Fulton Georgia\n") && {
    while read -u ${DEV##*/} FIELD1 FIELD2 FIELD3; do
        echo "$FIELD1 lies in $FIELD2 County, $FIELD3."
    done
}

示例输出:

Cincinnati lies in Hamilton County, Ohio.
Atlanta lies in Fulton County, Georgia.

在我的实际应用中,"some commands"比较复杂,但上面的例子抓住了问题的本质。

需要进程替换 <()。进程替换的替代方法无济于事。

使用运算符 <.

重定向到循环的标准输入
while read city county state; do
    echo "$city lies in $county County, $state."
done < <(echo -ne "Cincinnati Hamilton Ohio\nAtlanta Fulton Georgia\n")

输出:

Cincinnati lies in Hamilton County, Ohio.
Atlanta lies in Fulton County, Georgia.

请注意,在此示例中,管道也能正常工作。

echo -ne "Cincinnati Hamilton Ohio\nAtlanta Fulton Georgia\n" |
    while read city county state
do
    echo "$city lies in $county County, $state."
done

另外,环境变量(如PATH)和其他特殊变量(如RANDOM)应保留大写变量名。描述性的变量名总是好的。

几乎没有可移植的选择。 'right' 的选择取决于具体情况。特别是,它取决于生成输入数据的时间和输入的大小。特别是:

  • 如果处理数据需要很多时间,您希望在数据生成和 'while' 循环之间进行并行处理。这将导致增量处理,并且在开始输出数据处理之前不必等待所有输入数据处理。

  • 如果输入非常大(并且不适合 shell 变量),您可能别无选择,只能强制使用实际管道。当数据是二进制、Unicode 或类似数据时也是如此 - 其中 bash 变量将不起作用。

映射到原始问题 - PRODUCE = echo Cincinnati ...,以及 CONSUME - echo "$city ..."

对于简单的情况(小输入,快速 produce/consume),下面的方法将起作用。 Bash 将 运行 他们顺序地:生产然后消费。

while read ... ; do
    CONSUME
done <<< "$(PRODUCE)"

对于复杂的情况(输入大,或者生产消费慢),可以使用下面的方法来请求并行执行

while read ... ; do
    CONSUME
done < <(PRODUCE)

对于复杂(循环、条件等)或长(多行)的 PRODUCE 代码,考虑将其移动到一个函数中,而不是将它们内联到循环命令中。

function produce {
    PRODUCE
}

while read ... ; do
    CONSUME
done < <(produce)