哪个 shell 解释器运行不带 shebang 的脚本?

哪个 shell 解释器运行不带 shebang 的脚本?

假设我帐户的默认 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/shvs )),或者调用自己/usr/xpg4/bin/sh存储的 shell,如使用它的 命令或许多其他命令(将调用用户的登录 shell 而不是)。$SHELLvi!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 )来解释它。execexecve()bashshyashshargv[0]sh

zsh基于, pdksh,ash的 shell 通常会调用sh(其路径在编译时确定)。

对于cshtcsh(以及sh一些早期 BSD 的 ),如果文件的第一个字符是#,那么它们将执行自身来解释它,sh否则。这可以追溯到 Shebang 之前的时代,当时csh确实识别#为注释,但不识别 Bourne shell,因此#暗示它是一个 csh 脚本。

fish(至少版本 2.4.0),如果execve()失败则仅返回错误(它不会尝试将其视为脚本)。

某些 shell(例如bashAT&T ksh)将首先尝试试探性地确定该文件是否可能是脚本。因此,您可能会发现,如果脚本的前几个字节中有 NUL 字符,某些 shell 将拒绝执行该脚本。

另请注意,如果execve()ENOEXEC 失败,但文件确实有 shebang 行,某些 shell 会尝试自行解释该 shebang 行。

举几个例子:

  • $SHELL是时/bin/bashxterm -e 'myscript with args'将由in模式myscript解释。而使用,将使用,因此脚本将由 解释。bashshxterm -e myscript with argsxtermexecvp()sh
  • su -c myscript在 Solaris 10 上,其中root的登录 shell 是/bin/sh/bin/sh是 Bourne shell 将由myscriptBourne 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/shEven with 。/usr/xpg4/bin/find
  • csh -c myscriptcsh如果它以 开头#,则将其解释为sh否则。

总而言之,如果您不知道如何以及通过什么调用该脚本,则无法确定将使用哪个 shell 来解释该脚本。

在任何情况下,仅read -pbash语法,因此您需要确保脚本被解释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。

相关内容