为什么在bash中将stdin重定向到while循环中?

时间:2017-01-14 14:10:25

标签: linux bash shell io-redirection

考虑以下示例脚本:

#!/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

stdout重定向为filedescriptor 3的输入是什么意思? 至少在Bash中,如果省略则似乎没有任何区别。如果在bash以外的任何其他shell中执行它会产生任何影响吗?

更新

对于那些想知道它来自哪里的人来说,它是来自Debian的cryptdisks_start脚本的简化示例。

1 个答案:

答案 0 :(得分:7)

这里的明确意图是通过确保其标准输入来自其他地方来阻止do_somethingsample.text流中读取。 如果您在使用或不使用重定向时没有看到行为上的差异,那是因为do_something实际上并未在测试中从stdin读取。

如果您同时从同一个流中读取readdo_something,那么do_something所使用的任何内容都无法用于read的后续实例 - - 当然,你输入do_something时会输入非法内容,从而导致诸如加密密钥被尝试的后果(如果真实用例类似于cryptmount) ,&amp; 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<&13<&0相比是不好的做法,因为它假定没有基础,stdout也可以用作输入 - 但它成功实现了这个目标。

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

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,但这意味着如果我们运行stdout连接到FIFO的只写端(或任何其他只写),我们的代码就不会再被破坏接口)而不是直接到TTY。

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