在读取循环和标准时猛击 - 强制嵌套命令的 stdin 到 tty?



我终于在进入一段时间读取循环时遇到了令人惊讶的 stdin 行为。

请考虑以下事项:

find . | while read file; 
do
  echo "==[$file]==";
  cat;
done

在这种情况下,cat只是从 STDIN 接收输入的任何命令的替身。 令人惊讶的是(至少对我来说(cat的STDIN实际上来自find,所以它吞噬了其余的find输出。

假设有人想直接从 tty 与 cat 的命令进行交互。 例如,假设你想运行一个脚本而不是cat,该脚本可能会询问你想要以交互方式回答的问题("<file> exists: Overwrite? [y/n]"(。

有没有办法强制内部命令的 STDIN 成为 tty?

我发现了很多类似的问题,包括:为什么要在 bash 中的读取循环中重定向 stdin?

但是我无法很好地理解答案,无法使其工作。

(编辑:鉴于对另一个问题的澄清,我现在认为这是该问题的副本。

在下面的示例中,我将用问题较少的东西替换cat

read_a_line() { local line; read -r line; echo "Read line: $line"; }

这样,它在每个循环调用中只读取行输入,而不是一直读取到 EOF。否则,我试图将更改保持在最低限度,以专注于眼前的问题。

请参阅 BashFAQ #24 讨论为什么最好从流程替换重定向到循环而不是管道到循环。


首先,您可以简单地从/dev/tty

重定向
find . | while read file; 
do
  echo "==[$file]=="
  read_a_line </dev/tty
done

其次,您可以将 stdin 复制到不同的文件描述符,并在以后重复使用它:

exec 3<&0  # make FD 3 a copy of FD 0
find . | while read file; do
  echo "==[$file]=="
  read_a_line <&3
done
exec 3<&- # close FD 3 now that we're done with it

第三,你可以尝试两者兼而有之——尝试使FD 3(或你选择的高于2的任何其他FD(对/dev/tty开放,但如果失败,则将其作为原始stdin的备份。

exec 3</dev/tty || exec 3<&0
find . | while read file; do
  echo "==[$file]=="
  read_a_line <&3
done
exec 3<&-

这个例子可能会有所帮助:

{
while IFS= read -r -d '' file
do
    read -u3 -p "what to do with: [$file]?> " action
    printf "got [$action] for the [$file]nn"
done < <(find . -print0)
} 3<&0
  • 对于整个脚本,标准被编辑为FD3
  • 内部whilefind重定向
  • read从FD3读取 - 例如从终端读取

最新更新