有什么方法可以从命令输出中读取行?


8

我有一个预处理命令来输出文件

./preprocess.sh > preprocessed_file 

并且preprocessed_file将像这样使用

while read line
do

    ./research.sh $line &

done < preprocessed_file 

rm -f preprocessed_file

有什么方法可以将输出定向到while read line零件,而不是输出到preprocessed_file?我认为除了使用此temp之外,还有更好的方法preprocessed_file

Answers:


8

您可以使用bash 进程替换

while IFS= read -r line; do
  ./research.sh "$line" &
done < <(./preprocess.sh)

流程替换的一些优点:

  • 无需保存临时文件。
  • 更好的性能。从另一个进程读取数据通常比写入磁盘快,然后再读回。
  • 由于与参数和变量扩展,命令替换和算术扩展同时执行时可以节省计算时间

左双箭头(<<)是什么意思?
Marcus Thornton 2014年

@MarcusThornton:<是重定向,<(...)而是进程替换语法。您应该阅读:gnu.org/software/bash/manual/html_node/…了解更多详细信息。
cuonglm 2014年

得到它了。<(...)是语法的一部分。
Marcus Thornton 2014年

2
不一定更快。因为从管道read读取时必须一次读取一个字节,但它可以通过读取较大的块来优化内容,并在从常规文件读取时向后查找。最好是尽可能地while read完全避免循环。另请注意,您需要IFS= read -r line将该行读入$line。并且$line在此处不加引号(调用split + glob运算符)可能没有任何意义。
斯特凡Chazelas

1
@mikeserv,命令通常在到达终端时对其输出进行行缓冲(与全缓冲相反)。在这里,我说的是,read内置的Shell每次从管道读取时都会读取一个字符(不管管道另一端的内容如何,​​无论如何read也无法知道),这就是while read循环非常慢的原因之一。
斯特凡Chazelas

15

是! 您可以使用过程管道|

./preprocess.sh |
    while IFS= read -r line
    do
        ./research.sh "$line" &
    done

过程管道将一个过程的标准输出(stdout)传递到下一个过程的标准输入(stdin)。

您可以选择在a后面放置换行符|,并将命令扩展到下一行。

注意:a|b等同于b < <(a),但没有魔幻文件,并且可读性更高,尤其是当管道变长时。

a|b|c 相当于 c < <(b < <(a))

a|b|c|d|ee < < (d < <(c < <(b < <(a))))


3
注意:这种带有管道的解决方案的优点是比过程替换更可移植(某些破折号等POSIX shell不支持)。仍然涉及可移植性,管道的右侧可以在子shell中执行(这取决于shell),因此任何副作用(例如设置变量)都不会影响shell脚本的环境。
vinc17 2014年

通常将变量引用$line放在双引号中比较安全(例如,在脚本中./research.sh "$line" &)。
G-Man说'恢复莫妮卡'

1
@ G-Man可能不在这种情况下。如果research.sh与命令行参数数组一起使用并且$line例如为“ one two”,并且第一个参数为“ one”,第二个参数“ two”,则使用引号$line将使其变为不可能,相反,第一个参数为“一个二”,不会再有第二个……
goldilocks 2014年

2
a|b相当于b < <(a) ”-接近,但不完全相同。在管道版本中,管道的两端都在子外壳程序中运行,而在进程替代版本中,只有替换的进程在子a外壳程序中运行,但是在当前执行的外壳程序级别的范围内运行。这对设置的变量范围具有重要意义a
Digital Trauma 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.