哪个外壳解释器运行没有shebang的脚本?


17

假设我帐户的默认外壳是zsh,但是我打开了终端并启动了bash并执行了一个名为的脚本prac002.sh,那么该外壳解释器将用于执行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

该脚本在顶部说什么?
Michael Homer

@MichaelHomer:没事。我刚刚编辑了问题以添加脚本的内容。该脚本具有可执行权限。
7_R3X

如果要使用当前外壳程序,请在. prac002.sh脚本中将其点在源文件中,如中所示,假设您的脚本位于当前目录中。
–carpcarpy

1
运行. ./prac002.sh,它将与当前外壳一起运行,即点(.),空格()和脚本路径。这称为点源脚本。;-)
thecarpy

Answers:


19

因为脚本不是以#!表示使用哪个解释器的shebang行开头,所以POSIX表示

如果execl()由于与POSIX.1-2008的系统接口卷中定义的[ENOEXEC]错误等效的错误而导致函数失败,则外壳程序将执行等效于执行命令的命令该命令调用的外壳程序将以搜索所得的路径名作为第一个操作数,所有剩余的参数都传递给新的shell,但新的shell中“ $ 0”的值可以设置为命令名。如果可执行文件不是文本文件,则外壳程序可以绕过此命令执行。在这种情况下,它将写入错误消息,并返回退出状态126。

这种表述有点模棱两可,不同的外壳有不同的解释。

在这种情况下,Bash将使用自身运行脚本。另一方面,如果您从zsh运行它,则zsh将使用sh(无论系统上是什么)。

您可以通过在脚本中添加以下几行来验证这种情况下的行为:

echo $BASH_VERSION
echo $ZSH_VERSION

您会注意到,无论使用哪种shell,第一行都从Bash输出您的版本,而第二行则从不输出任何内容。

  • 如果您/bin/sh说的是,dash则从zsh或破折号执行脚本时,任何一行都不会输出任何内容。
  • 如果您/bin/sh是Bash的链接,则在所有情况下都将看到第一行输出。
  • 如果/bin/sh是一个不同版本的Bash比你直接使用,你会看到不同的输出,当你从bash的直接和zsh中运行该脚本。

ps -p $$rools的答案中命令还将显示有关shell用于执行脚本的命令的有用信息。


我赞成,但是,如果没有指定shebang,则无论您指定了哪种默认shell,它都会使用DEFAULT SHELL。这就是为什么你应该总是,恕我直言,指定一个shebang。
–carpy

4
事实并非如此,这才是答案的全部重点。您的第二句话肯定是正确的。
Michael Homer

没错,bash自行运行它,我大多数盒子上的默认外壳恰好是.... bash,从那里我很困惑。我刚刚在Solaris 8上使用ksh作为默认外壳进行了测试,果然,bash以bash的身份运行它...今天学到了一些新东西,谢谢(赞成!);-)
thecarpy

谢谢。execl()当shell脚本不包含shebang并执行为时,是否会发生调用失败scriptname?在执行shell脚本时不会发生这种情况bash scriptname吗?当shell脚本包含shebang并执行为时,是否不会发生scriptname
所有StackExchange


7

由于文件不是系统识别的任何可执行文件类型,并且假设您具有执行该文件的权限,因此execve()系统调用通常会失败,并显示ENOEXEC不是可执行文件)错误。

然后发生什么取决于用于执行命令的应用程序和/或库函数。

例如,可以是一个外壳,即execlp()/ execvp()libc函数。

大多数其他应用程序在运行命令时将使用其中任何一个。他们将通过system("command line")libc函数调用外壳程序,该函数通常将调用sh该命令行以解析该命令行(可以在编译时确定其路径(例如/bin/shvs /usr/xpg4/bin/sh在Solaris上)),或者$SHELL像它们自己那样调用存储在其中的shell vi使用其 !命令或xterm -e 'command line'其他命令(su user -c将调用用户的登录shell而不是$SHELL)。

通常,不以shebang开头的无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 ksh,Bourne Shell通常会exec在模拟a之后,自己解释脚本本身(除非使用子进程,否则将在子进程中)execve(),即未设置所有未导出的变量,关闭所有close-on-exec fds,删除所有自定义陷阱,别名,函数...(bash将以sh模式解释脚本)。yash将执行自身(在模式下sh也是argv[0]这样sh)以对其进行解释。

zshpdkshash基壳通常将调用sh(在编译时间,其中所确定的路径)。

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

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

某些shell(例如bash或AT&T ksh)将首先尝试试探性地确定文件是否可能是脚本。因此,您可能会发现,如果前几个字节中包含NUL字符,则某些外壳程序将拒绝执行脚本。

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

举几个例子:

  • 如果$SHELL/bin/bashxterm -e 'myscript with args'将已myscript被解释bashsh方式。与一起使用时xterm -e myscript with argsxterm将使用execvp()来解释脚本sh
  • su -c myscript在Solaris 10中,root登录外壳是Bourne外壳/bin/sh/bin/sh现在是Bourne外壳将由Bourne外壳myscript解释。
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'在Solaris 10上,它将由解释/usr/xpg4/bin/sh(与相同/usr/xpg4/bin/env myscript)。
  • find . -prune -exec myscript {} \;在Solaris 10上(使用execvp()),即使在POSIX环境中(一致性错误),也将使用/bin/sh即使使用来解释它/usr/xpg4/bin/find
  • csh -c myscriptcsh如果以开头#,则以解释,sh否则为。

总而言之,如果您不知道该脚本的调用方式和调用方式,则无法确定将使用哪个shell来解释该脚本。

在任何情况下,read -p都是bash-only语法,因此您需要确保脚本由解释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"

1

当您没有任何#!(称为shebang)行时,将使用sh。要进行检查,可以运行以下脚本。

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

在我的电脑上

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

即使我的默认外壳是zsh。它使用bash,因为在我的机器上,sh命令由bash实现。


1
你为什么要反对呢?请解释,否则它是无用的...
rools '18

仇恨者(沉默的匿名唐纳德族人)会讨厌。我从您的答案中学到了一些东西,所以赞。:-)
Mac
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.