从终端读取输入的脚本。如果使用 stdout 重定向调用脚本,如何告诉脚本打印到 tty 和 stdout?

从终端读取输入的脚本。如果使用 stdout 重定向调用脚本,如何告诉脚本打印到 tty 和 stdout?

我有一个脚本要求用户在终端中输入,因此,如果使用重定向到文件的 stdout 调用脚本,则用户在提示符上没有上下文。所以我想将标准输出复制到两个站点。

该脚本输出一个微积分,因此允许 stdout 重定向仍然有意义。

但是如果提供了 stdin(类似于 yes 命令或非交互式上下文,如 cron),则完全允许输出在 tty 中不需要是有意义的。

所以我想做一些类似的逻辑:

If (test -t 0 && ! test -t 1);
  // Tell stdout to print everything  to tty as well

这可能吗?还是根本上就不合适?

答案1

请注意,如果您关心的是提示字符串,它通常应该转到 stderr。这就是大多数公用事业公司在发出提示时所做的事情。例如,请参阅read 'var?prompt或 ksh/zsh/yashread -p prompt varbash

这样,它就与正常输出分开,然后您可以安全地重定向。

要仍然执行您所要求的操作,并在 stdin 位于 tty 上但 stdout 不在 tty 上时将输出发送到控制 tty,在包括 ksh93、zsh 或 bash 在内的一些 shell 中,您可以执行以下操作:

if [[ -t 0 && ! -t 1 ]]; then
  exec > >(tee /dev/tty)
fi

但由于它tee是在后台运行的,您可能会发现它到终端的输出在错误的时间出现。例如:

$ zsh -c 'exec > >(tee /dev/tty); print before; read "var?Prompt: "; print -r "after: $var"' > /dev/null
Prompt: before
what?
after: what?

before到达 tty read发出了提示。

因此,要在全局范围内执行这些重定向,您需要对每个命令执行此操作,或者至少对发出提示或自行写入 stderr 或终端的命令之间的每组命令执行此操作,甚至是 stdout/stderr每个命令可能会以奇怪的顺序显示。

#! /bin/zsh -
if [[ -t 0 && ! -t 1 ]]; then
  alias -g '{#}=>&1 > /dev/tty'
else
  alias -g '{#}='
fi
print before {#}
read 'var?prompt: '
print -r after: $var {#}

tee(当 fd 被重定向多次时,这里使用 zsh 的内置工具)。

相关内容