【问题标题】:Why redirect stdin inside a while read loop in bash?为什么在 bash 的 while 读取循环中重定向标准输入?
【发布时间】:2017-03-16 03:32:57
【问题描述】:

考虑以下示例脚本:

#!/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 脚本的简化示例。

【问题讨论】:

  • 这个脚本来自哪里?我很好奇作者的意图。正如您所观察到的,由于do_something() 不使用其标准输入,&lt;&amp;3 == 0&lt;&amp;3 没有任何区别。
  • 您对文件描述符的使用对您要执行的整体逻辑没有影响!
  • 如果do_something 确实尝试从标准输入中读取,则会出现一个棘手的死锁。
  • 基本上,当调用do_something 时,还没有将任何内容写入循环的标准输出,因此在其标准输入上没有任何内容可供读取。
  • @nautical,在基于 Web 的 SCM 界面(sourceforge、github、launchpad 等)的时代,大多数项目都有一个单独的源代码行可以直接在 Web 上链接——在鉴于此,“下载这个 tarball,解压它,然后找到包含 [...] 的文件”不是很合理的请求。

标签: linux bash shell io-redirection


【解决方案1】:

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

如果您同时从同一个流中读取 readdo_something,那么 do_something 使用的任何内容都将无法用于 read 的后续实例——当然,您d 在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&lt;&amp;0 相比,3&lt;&amp;1 是不好的做法,因为它毫无根据地假设 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 问题:

等等

【讨论】:

  • 抱歉回复晚了。我终于有时间回到这个问题了。我知道如果stdin 没有正确重定向/保护,read 可以产生“有趣”。让我难过的是stdout 的重定向作为输入。似乎作者试图强制执行一些类似管道的行为。我以前从未见过这种情况。真的有正当理由重定向stdout 我的问题吗?
  • @nautical,“ever”涵盖了很多情况。也许标准输入对/dev/null 开放,标准输出是开放读/写(所以你可以 也从中读取),并且你没有控制 TTY。但这是一个极端情况,有人必须知道他们的脚本将被用在那个极端情况下才有意义。
  • @CharlesDuffy:在您添加代码 cmets 之前,我之前无法理解解决方案。 cmets现在帮助我理解它。谢谢。
猜你喜欢
  • 2017-11-25
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-30
相关资源
最近更新 更多