为什么在bash的while读取循环中重定向stdin? [英] Why redirect stdin inside a while read loop in bash?

查看:133
本文介绍了为什么在bash的while读取循环中重定向stdin?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下示例脚本:

#!/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中执行它,是否有效果?

What is the purpose of redirecting stdout as input for filedescriptor 3? At least in Bash, it does not seem to make any difference if omitted. Does it have any effect if it is executed in any other shell than bash?

更新

对于那些想知道它来自哪里的人,它是Debian的 cryptdisks_start 脚本.

For those wondering where this is from, it is a simplified sample from Debian's cryptdisks_start script.

推荐答案

此处的明确意图是通过确保其stdin来自其他地方来防止do_somethingsample.text流中读取. 无论您是否看到重定向或不重定向的行为差异,这都是因为do_something实际上不是在测试中从stdin读取的内容.

The clear intent here is to prevent do_something from reading from the sample.text stream, by ensuring that its stdin is coming from elsewhere. If you're not seeing differences in behavior with or without the redirection, that's because do_something isn't actually reading from stdin in your tests.

如果同时从同一流中读取readdo_something,则do_something消耗的任何内容将对read的后续实例不可用-当然,您可以d将不合法的内容输入到do_something的输入中,从而导致诸如尝试错误的加密密钥(如果实际用例类似于cryptmount之类)& c的后果.

If you had both read and do_something reading from the same stream, then any content consumed by do_something wouldn't be available to a subsequent instance of read -- and, of course, you'd have illegitimate contents fed on input to do_something, resulting in consequences such as a bad encryption key being attempted (if the real-world use case were something like 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<&0相比,3<&1是一种不好的做法,因为它假设没有基础的stdout也可以用作输入-但确实在那个目标上取得成功.

Now, it's buggy -- 3<&1 is bad practice compared to 3<&0, inasmuch as it assumes without foundation that stdout is something that can also be used as input -- but it does succeed in that goal.

顺便说一句,我会这样写:

By the way, I would write this more as follows:

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.

It's a little more verbose, needing to explicitly close FD 3, but it means that our code is no longer broken if we're run with stdout attached to the write-only side of a FIFO (or any other write-only interface) rather than directly to a TTY.

至于这种做法可以防止的错误,这是很常见的错误.例如,请参阅以下有关它的StackOverflow问题:

As for the bug that this practice prevents, it's a very common one. See for example the following StackOverflow questions regarding it:

  • Shell script while read line loop stops after the first line
  • ssh breaks out of while-loop in bash
  • Bash while loop stops for no reason?

这篇关于为什么在bash的while读取循环中重定向stdin?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆