为什么要在 bash 中的 while read 循环内重定向 stdin?

问题描述 投票:0回答:2

考虑以下示例脚本:

#!/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
重定向为文件描述符 3 的输入的目的是什么? 至少在
Bash
中,省略似乎没有什么区别。如果在
bash
以外的其他shell中执行有什么影响吗?

更新

对于那些想知道这是从哪里来的人,它是 Debian 的

cryptdisks_start
脚本的简化示例。

linux bash shell io-redirection
2个回答
12
投票

这里的明确意图是通过确保其标准输入来自其他地方来防止

do_something
sample.text
流中读取。 如果您在有或没有重定向的情况下没有看到行为差异,那是因为
do_something
实际上并未在测试中从 stdin 读取。

如果您同时从同一个流中读取

read
do_something
,那么
do_something
消耗的任何内容将无法用于
read
的后续实例 - 当然,您也可以
do_something
的输入中包含非法内容,导致尝试使用错误的加密密钥等后果(如果现实世界的用例类似于
cryptmount
),等等。

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
是不好的做法,因为它在没有基础的情况下假设 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,但这意味着如果我们使用附加到 FIFO(或任何其他只写接口)的只写端的 stdout 运行,我们的代码就不再被破坏而不是直接发送至 TTY。


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

等等


0
投票

如前所述,这是关于分离流以供读取,但有一个更简单的通用格式。

首先去掉无用的

cat
,将
while
循环保留在主脚本的范围内。 这使得循环的内容能够像往常一样与 stdin、stdout 和 stderr 交互,而不会受到从文本文件读取的 arg 的干扰。例如:

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

read
可能需要
-r
标志来防止反斜杠转义序列解释。 然后
do_something
接收标准输入(或带有
<&1
的标准输出,如示例所示,如果可能或需要的话)。 如果
sample.txt
应该是命令的输出,请使用进程替换,如
done 3< <(arg_generating_command)

© www.soinside.com 2019 - 2024. All rights reserved.