shell 脚本不是输入终端的击键记录。如果你写一个这样的脚本:
command1
bash
command2
这并不意味着脚本会切换到bash,然后在不同的shell中执行command2。这意味着将运行bash。如果有控制终端,该 bash 将向您显示提示并等待输入命令。您必须输入 exit 才能退出该 bash。只有这样,原始脚本才会继续使用command2。
没有办法在中途将脚本切换到不同的 shell。有一些方法可以模拟这一点。脚本可以使用不同的 shell 重新执行自身。为了做到这一点,脚本必须包含检测它正在被重新执行的逻辑,以便它可以防止再次重新执行自身,并跳过一些不应该运行两次的代码。
在this script 中,我实现了这样一个重新执行黑客。它由以下几行组成:
#
# The #!/bin/sh might be some legacy piece of crap,
# not even up to 1990 POSIX.2 spec. So the first step
# is to look for a better shell in some known places
# and re-execute ourselves with that interpreter.
#
if test x$txr_shell = x ; then
for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
if test -x $shell ; then
txr_shell=$shell
break
fi
done
if test x$txr_shell = x ; then
echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
txr_shell=/bin/sh
fi
export txr_shell
exec $txr_shell $0 ${@+"$@"}
fi
txr_shell 变量(不是标准变量,我的发明)是该逻辑检测到它已被重新执行的方式。如果变量不存在,那么这是原始执行。当我们重新执行时,我们 export txr_shell 因此重新执行的实例将具有此环境变量。
该变量还保存了shell的路径;稍后在脚本中使用;它作为SHELL 变量传递给Makefile,以便构建配方使用相同的shell。在上述逻辑中,txr_shell 的内容无关紧要;它用作布尔值:它存在或不存在。
上面代码 sn-p 中的编程风格是故意编码为在非常旧的 shell 上工作。这就是为什么使用test x$txr_shell = x 而不是现代语法[ -z "$txr_shell" ],以及为什么使用${@+"$@"} 而不仅仅是"$@"。
在脚本中的这一点之后不再使用这种样式,因为
多亏了重新执行的技巧,脚本的其余部分可以在一些不错的、相当现代的 shell 中运行。