假设我帐户的默认 shell 是 zsh,但我打开终端并启动 bash 并执行名为 的脚本prac002.sh
,将使用哪个 shell 解释器来执行该脚本,zsh 还是 bash?考虑以下示例:
papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf:
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh
papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)
papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh
Enter username : Rex
Rex
# Which interpreter did it just use?
**编辑:**这是脚本的内容
papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh
read -p "Enter username : " uname
echo $uname
答案1
因为脚本不以#!
shebang 行开头来指示要使用哪个解释器,POSIX 说:
如果
execl()
由于与 POSIX.1-2008 的系统接口卷中定义的 [ENOEXEC] 错误等效的错误,函数失败,shell 应执行一个命令,相当于使用搜索结果的路径名作为其第一个操作数来调用 shell,将任何剩余参数传递给新 shell,但新 shell 中的“$0”值可以设置为命令名称。如果可执行文件不是文本文件,shell 可能会绕过此命令执行。在这种情况下,它应写入一条错误消息,并应返回退出状态 126。
这种措辞有点含糊,不同的 shell 有不同的解释。
在这种情况下,Bash 将使用自身运行脚本。另一方面,如果您从 zsh 运行它,zsh 会使用sh
(无论您的系统上有什么)。
您可以通过将以下行添加到脚本来验证这种情况下的行为:
echo $BASH_VERSION
echo $ZSH_VERSION
您会注意到,在 Bash 中,第一行输出您的版本,而第二行从不显示任何内容,无论您使用哪种 shell。
- 如果您
/bin/sh
是,比如说,dash
那么当从 zsh 或 dash 执行脚本时,这两行都不会输出任何内容。 - 如果您
/bin/sh
是 Bash 的链接,则在所有情况下您都会看到第一行输出。 - 如果
/bin/sh
是一个不同版本与直接使用 Bash 相比,当您直接从 bash 和从 zsh 运行脚本时,您会看到不同的输出。
这ps -p $$
来自 rools 答案的命令还将显示有关 shell 用于执行脚本的命令的有用信息。
答案2
由于该文件不是系统识别的任何类型的可执行文件,并且假设您有执行该文件的权限,系统execve()
调用通常会失败并显示ENOEXEC
(不是可执行文件) 错误。
接下来会发生什么取决于用于执行命令的应用程序和/或库函数。
例如,它可以是 shell、execlp()
/ execvp()
libc 函数。
大多数其他应用程序在运行命令时都会使用其中任何一个。例如,他们将通过system("command line")
libc 函数调用 shell,该函数通常会调用sh
来解析该命令行(其路径可以在编译时确定(如Solaris 上的/bin/sh
vs )),或者调用自己/usr/xpg4/bin/sh
存储的 shell,如使用它的 命令或许多其他命令(将调用用户的登录 shell 而不是)。$SHELL
vi
!
xterm -e 'command line'
su user -c
$SHELL
通常,不以 开头的无 shebang 文本文件#
被视为sh
脚本。但具体情况sh
会有所不同。
execlp()
/ execvp()
,execve()
返回时ENOEXEC
通常会调用sh
它。对于具有多个标准的系统sh
,因为它们可以符合多个标准,这sh
通常在编译时确定(应用程序使用execvp()
/execlp()
通过链接不同的代码块,该代码块引用了不同的路径sh
)。例如,在 Solaris 上,这将是/usr/xpg4/bin/sh
(标准,POSIX sh
)或/bin/sh
(Solaris 10 及更早版本上的 Bourne shell(过时的 shell),Solaris 11 中的 ksh93)。
当谈到贝壳时,有很多变化。bash
, AT&T ,Bourne shell 通常会在模拟 a 后自行解释脚本(除非使用ksh
子进程) ,即取消设置所有未导出的变量,关闭所有 close-on-exec fds,删除所有自定义陷阱,别名、函数...(将在模式下解释脚本)。将执行自身(在模式下使用as so )来解释它。exec
execve()
bash
sh
yash
sh
argv[0]
sh
zsh
基于, pdksh
,ash
的 shell 通常会调用sh
(其路径在编译时确定)。
对于csh
和tcsh
(以及sh
一些早期 BSD 的 ),如果文件的第一个字符是#
,那么它们将执行自身来解释它,sh
否则。这可以追溯到 Shebang 之前的时代,当时csh
确实识别#
为注释,但不识别 Bourne shell,因此#
暗示它是一个 csh 脚本。
fish
(至少版本 2.4.0),如果execve()
失败则仅返回错误(它不会尝试将其视为脚本)。
某些 shell(例如bash
AT&T ksh
)将首先尝试试探性地确定该文件是否可能是脚本。因此,您可能会发现,如果脚本的前几个字节中有 NUL 字符,某些 shell 将拒绝执行该脚本。
另请注意,如果execve()
ENOEXEC 失败,但文件确实有 shebang 行,某些 shell 会尝试自行解释该 shebang 行。
举几个例子:
- 当
$SHELL
是时/bin/bash
,xterm -e 'myscript with args'
将由in模式myscript
解释。而使用,将使用,因此脚本将由 解释。bash
sh
xterm -e myscript with args
xterm
execvp()
sh
su -c myscript
在 Solaris 10 上,其中root
的登录 shell 是/bin/sh
,/bin/sh
是 Bourne shell 将由myscript
Bourne shell 进行解释。/usr/xpg4/bin/awk 'BEGIN{system("myscript")'
在 Solaris 10 上,它会被解释为/usr/xpg4/bin/sh
(与 相同/usr/xpg4/bin/env myscript
)。find . -prune -exec myscript {} \;
在 Solaris 10 上(使用) ,即使在 POSIX 环境中(一致性错误),execvp()
也会将其解释为/bin/sh
Even with 。/usr/xpg4/bin/find
csh -c myscript
csh
如果它以 开头#
,则将其解释为sh
否则。
总而言之,如果您不知道如何以及通过什么调用该脚本,则无法确定将使用哪个 shell 来解释该脚本。
在任何情况下,仅read -p
是bash
语法,因此您需要确保脚本被解释bash
(并避免误导性.sh
扩展)。您知道可执行文件的路径bash
并使用:
#! /path/to/bash -
read -p ...
或者您可以尝试使用以下命令来$PATH
查找bash
可执行文件(假设已安装):bash
#! /usr/bin/env bash
read -p ...
(env
几乎无处不在/usr/bin
)。或者,您可以使其与 POSIX+Bourne 兼容,在这种情况下您可以使用/bin/sh
.所有系统都会有一个/bin/sh
.在大多数情况下,它将(大部分)与 POSIX 兼容,但您仍然可能偶尔会发现 Bourne shell。
#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"
答案3
要检查使用的是哪个 shell,可以运行以下脚本。
ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe
在我的电脑上我得到
PID TTY TIME CMD
13718 pts/16 00:00:00 sh
The real shell is: /usr/bin/bash
即使我的默认 shell 是桀骜。它用巴什因为在我的机器上,嘘命令的实现是通过巴什和桀骜精选嘘如果没有指定 shebang。