使用Bash的进程替换同步输出

时间:2016-04-29 10:52:36

标签: bash process synchronization substitution

我必须多次调用一个不灵活的外部工具,该工具将一些输入数据作为参数,并将输出文件作为参数写入处理数据,例如:

some_prog() {  echo "modified_$1" > "$2"; }

对于不同的输入,我想调用some_prog,过滤输出并将所有调用的输出写入同一文件" out_file"。另外,我想在每次调用some_prog之前向输出文件添加标题行。给出以下虚拟过滤器:

slow_filter() { 
    read input; sleep "0.000$(($RANDOM % 10))"; echo "filtered_$input"
}

我写了以下代码:

rm -f out_file
for input in test_input{1..8}; do
    echo "#Header_for_$input"  >> "out_file"
    some_prog $input >( slow_filter  >> "out_file" )
done

但是,这将产生一个像这样的out_file:

#Header_for_test_input1
#Header_for_test_input2
#Header_for_test_input3
#Header_for_test_input4
#Header_for_test_input5
#Header_for_test_input6
#Header_for_test_input7
#Header_for_test_input8
filtered_modified_test_input4
filtered_modified_test_input1
filtered_modified_test_input2
filtered_modified_test_input5
filtered_modified_test_input6
filtered_modified_test_input3
filtered_modified_test_input8
filtered_modified_test_input7

我预期的输出是:

#Header_for_test_input1
filtered_modified_test_input1
#Header_for_test_input2
filtered_modified_test_input2
#Header_for_test_input3
filtered_modified_test_input3
#Header_for_test_input4
filtered_modified_test_input4
#Header_for_test_input5
filtered_modified_test_input5
#Header_for_test_input6
filtered_modified_test_input6
#Header_for_test_input7
filtered_modified_test_input7
#Header_for_test_input8
filtered_modified_test_input8

我意识到>()进程替换了shell。有没有办法同步子壳的输出?或者这个问题有另一个优雅的解决方案吗?我想避免在每次迭代中写入不同文件的明显方法,因为在我的代码中,for循环有几十万次迭代。

2 个答案:

答案 0 :(得分:1)

将头写入进程替换,特别是在带有过滤器的命令组中,以便将连接的输出作为一个流写入out_file

rm -f out_file
for input in test_input{1..8}; do
    some_prog "$input" >( { echo "#Header_for_$input"; slow_filter; }  >> "out_file" )
done

由于进程替换是真正异步的,并且在执行循环的下一次迭代之前似乎没有办法等待它完成,我会使用一个显式的命名管道。

rm -f out_file pipe
mkfifo pipe
for input in test_input{1..8}; do
    some_prog "$input" pipe &
    echo "#Header_for_$input" >> out_file
    slow_filter < pipe >> out_file
done

(如果some_prog由于某种原因无法使用命名管道,则可以使用常规文件。在这种情况下,您不应该在后台运行该命令。)

答案 1 :(得分:1)

由于chepner使用命名管道的方法在我的“真实世界脚本”中似乎非常慢(比此解决方案慢约10倍),实现我想要的最简单,最安全的方法似乎是一个临时文件: / p>

rm -f out_file
tmp_file="$(mktemp --tmpdir my_temp_XXXXX.tmp)"
for input in test_input{1..8}; do
    some_prog "$input" "$tmp_file"
    {
        echo "#Header_for_$input"
        slow_filter < "$tmp_file"
    } >> out_file
done
rm "$tmp_file"

这样,临时文件tmp_file在每次迭代中都会被覆盖,这样如果系统的临时目录是RAM磁盘,它就可以保存在内存中。