为什么redirect标准input框在一个while bash中读取循环?

考虑下面的示例脚本:

#!/bin/sh do_something() { echo $@ return 1 } cat <<EOF > sample.text This is a sample text It serves no other purpose EOF cat sample.text | while read arg1 arg2 arg3 arg4 arg5; do ret=0 do_something "$arg1" "$sarg2" "$arg3" "$arg4" "$arg5" <&3 || ret=$? done 3<&1 

stdoutredirect为filedescriptor 3的input的目的是什么? 至less在Bash ,如果省略,似乎没有什么区别。 如果它在bash之外的任何其他shell中执行,它是否有效?

UPDATE

对于那些想知道这是从哪里来的,这是从Debian的cryptdisks_start脚本简化的示例。

这里明确的目的是防止do_somethingsample.text流中读取,通过确保它的stdin来自其他地方。 如果在没有重定向的情况下没有看到行为上的差异,那是因为在测试中do_something实际上并不是从标准输入读取的。

如果你同时从同一个流中readdo_something读取,那么do_something所消耗的任何内容将不能用于后续的read实例 – 当然,您将输入的内容非法输入do_something ,导致在后果如尝试一个坏的加密密钥(如果现实世界的用例像cryptmount ),&c。

 cat sample.text | while read arg1 arg2 arg3 arg4 arg5; do ret=0 do_something "$arg1" "$sarg2" "$arg3" "$arg4" "$arg5" <&3 || ret=$? done 3<&1 

现在,它是错误的 – 3<&1是不好的做法相比3<&0 ,因为它假设没有基础,标准输出也可以用作输入 – 但它成功的目标。


顺便说一下,我会写更多如下:

 exec 3</dev/tty || exec 3<&0 ## make FD 3 point to the TTY or stdin (as fallback) while read -a args; do ## |- loop over lines read from FD 0 do_something "${args[@]}" <&3 ## |- run do_something with its stdin copied from FD 3 done <sample.text ## \-> ...while the loop is run with sample.txt on FD 0 exec 3<&- ## close FD 3 when done. 

稍微冗长些,需要明确地关闭FD 3,但这意味着如果我们使用连接到FIFO的只写侧(或任何其他只写接口)的stdout来运行我们的代码,比直接到TTY。


至于这种做法阻止的错误,这是一个非常普遍的错误。 有关它的例子,请参阅以下StackOverflow问题:

  • 读取线路循环的shell脚本在第一行之后停止
  • ssh打破了在bash中的while循环
  • Bash while循环没有理由停止?

等等