36.1. 交互式和非交互式 Shell 及脚本

一个 交互式 shell 从用户的输入中读取命令,输入通过一个tty。除此之外,这样的 shell 在激活时会读取启动文件,显示提示符,并默认启用作业控制。用户可以与 shell 进行交互

运行脚本的 shell 始终是非交互式的。尽管如此,脚本仍然可以访问其tty。甚至可以在脚本中模拟交互式 shell。

#!/bin/bash
MY_PROMPT='$ '
while :
do
  echo -n "$MY_PROMPT"
  read line
  eval "$line"
  done

exit 0

# This example script, and much of the above explanation supplied by
# St�phane Chazelas (thanks again).

让我们将交互式脚本视为需要用户输入的脚本,通常通过 read 语句(参见示例 15-3)。“实际情况”实际上比这更复杂。目前,假设交互式脚本绑定到一个 tty,即用户从控制台或 xterm 调用的脚本。

Init 和启动脚本必然是非交互式的,因为它们必须在无人干预的情况下运行。许多管理和系统维护脚本同样是非交互式的。不变的重复性任务迫切需要通过非交互式脚本来实现自动化。

非交互式脚本可以在后台运行,但交互式脚本会挂起,等待永远不会到来的输入。通过使用 expect 脚本或嵌入式 here document 向作为后台作业运行的交互式脚本提供输入,可以解决这个难题。在最简单的情况下,重定向一个文件来为 read 语句提供输入(read variable <file)。这些特殊的方法使得通用脚本可以在交互式或非交互式模式下运行。

如果脚本需要测试它是否在交互式 shell 中运行,只需查找 prompt 变量,$PS1 是否已设置即可。(如果正在提示用户输入,则脚本需要显示提示符。)

if [ -z $PS1 ] # no prompt?
### if [ -v PS1 ]   # On Bash 4.2+ ...
then
  # non-interactive
  ...
else
  # interactive
  ...
fi

或者,脚本可以测试 $- 标志中是否存在 "i" 选项。

case $- in
*i*)    # interactive shell
;;
*)      # non-interactive shell
;;
# (Courtesy of "UNIX F.A.Q.," 1993)

然而,John Lange 描述了一种替代方法,使用 -t test 操作符

# Test for a terminal!

fd=0   # stdin

#  As we recall, the -t test option checks whether the stdin, [ -t 0 ],
#+ or stdout, [ -t 1 ], in a given script is running in a terminal.
if [ -t "$fd" ]
then
  echo interactive
else
  echo non-interactive
fi


#  But, as John points out:
#    if [ -t 0 ] works ... when you're logged in locally
#    but fails when you invoke the command remotely via ssh.
#    So for a true test you also have to test for a socket.

if [[ -t "$fd" || -p /dev/stdin ]]
then
  echo interactive
else
  echo non-interactive
fi

Note

可以使用 -i 选项或#!/bin/bash -i头来强制脚本以交互模式运行。请注意,这可能会导致不稳定的脚本行为,甚至在没有错误时也显示错误消息。