我终于在进入一段时间读取循环时遇到了令人惊讶的 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
- 内部
while
从find
重定向 read
从FD3读取 - 例如从终端读取