shebang是否确定运行脚本的shell?


84

这可能是一个愚蠢的问题,但我仍然要问。如果我宣布了射手

#!/bin/bash 

在的开头my_shell_script.sh,所以我总是必须使用bash调用此脚本吗?

[my@comp]$bash my_shell_script.sh

或者我可以使用例如

[my@comp]$sh my_shell_script.sh

我的脚本使用shebang确定正在运行的shell?kshShell 发生了同样的事情吗?我正在使用AIX。


6
您有一些困惑:执行“ _some_shell some_script”时,它会启动_some_shell并要求其解释some_script。所以不,如果您执行“ sh my_shell_script.sh”,它将不会解释shebang,而是会在sh中解释脚本。要使用shebang: chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory
Olivier Dulac

Answers:


117

家当 #!是一个的人类可读的实例幻数由字节串的0x23 0x21,它被用来由exec()家族的功能,以确定要执行的文件是否是一个脚本或二进制文件。当shebang存在时,exec()将运行shebang之后指定的可执行文件。

请注意,这意味着如果您通过在命令行上指定解释器来调用脚本(如在问题中给出的两种情况下所做的那样),exec()将执行在命令行中指定的解释器,甚至不会查看脚本。

因此,正如其他人指出的那样,如果要exec()调用在shebang行上指定的解释器,则脚本必须具有可执行位,并将其设置为./my_shell_script.sh

使用以下脚本可以轻松演示该行为:

#!/bin/ksh
readlink /proc/$$/exe

说明:

  • #!/bin/ksh定义ksh为解释器。

  • $$ 保留当前进程的PID。

  • /proc/pid/exe 是到进程可执行文件的符号链接(至少在Linux上;在AIX上,/ proc / $$ / object / a.out是到可执行文件的链接)。

  • readlink 将输出符号链接的值。

例:

:我在演示这个在Ubuntu上,在默认的外壳/bin/sh是一个符号链接/bin/dash并且/bin/ksh是一个符号链接/etc/alternatives/ksh,这又是一个符号链接/bin/pdksh

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash

感谢托马斯的回答。假设我们将脚本作为来自Node.js或Java或诸如此类的子进程启动。我们可以启动一个“ exec”进程,然后exec将运行shell脚本吗?我问beause我寻找这个问题的答案:stackoverflow.com/questions/41067872/...
亚历山大·米尔斯

1
@AlexanderMills exec()此答案中提到的是系统调用,命令exec是内置的Shell,这就是为什么您不能从Node.js或Java 调用exec 程序的原因。但是,例如Runtime.exec()用Java 调用的任何shell命令最终都会由exec()系统调用处理。
Thomas Nyman

嗯,是的,我的确很熟悉您刚才提到的Java API,我想知道是否有某种方法可以通过某种方式从Node.js调用较低级别的exec()调用
Alexander Mills

我可以想象@AlexanderMills child_process.{exec(),execFile(),spawn()}都将使用C来实现exec()(通过process)。
Thomas Nyman

10

是的,它确实。顺便说一下,这不是一个愚蠢的问题。我的答案参考在这里。用#开始脚本!

  • 这称为“ shebang”或“ bang”行。

  • 它不过是Bash解释器的绝对路径。

  • 它由数字符号和感叹号字符(#!)组成,后跟解释器的完整路径,例如/ bin / bash。

    Linux下的所有脚本都使用第一行中指定的解释器执行。几乎所有bash脚本通常都以#!/ bin / bash开头(假设Bash已安装在/ bin中),这确保了即使使用Bash来解释脚本,即使如果在另一个shell下执行。Shebang是由Dennis Ritchie在7版Unix和8版之间在Bell Laboratories推出的。然后,它也被添加到伯克利的BSD生产线中。

忽略翻译行(shebang)

如果您未指定解释器行,则默认通常为/ bin / sh。但是,建议您设置#!/ bin / bash行。


3
详细地说,内核仅知道如何执行静态链接的二进制文件以及在哪里可以找到其他人的解释器信息(二进制文件中的特殊字段或shebang行)。通常,执行shell脚本意味着将shebang行跟随到shell,然后将shell二进制文件中的DT_INTERP字段跟随到动态链接器。
Simon Richter

5
另请注意,这不仅限于shell脚本。所有基于文本的脚本文件都使用此文件。例如,#!/usr/bin/perl #!/usr/local/bin/python #!/usr/local/bin/ruby用于支持多个系统的另一个常见的shebang条目是使用env定位要使用的解释器,例如#!/usr/bin/env perl #!/usr/bin/env python
sambler

说到@sambler env,实际上应该首选哪个?Python和Perl env在shellscripts上经常使用,而通常会省略它,而shebang指向有问题的shell。
polemon

1
@polemon较少选择哪个,而在哪个路径上更多。在所有系统上,基本外壳程序位于同一路径中。可以在不同系统上的不同位置安装最新版本的perl和python,因此使用env可以使相同的shebang始终起作用,这就是为什么enl在perl和python脚本中使用的次数多于shell脚本。
sambler

env在$ PATH中找到程序有点麻烦。它没有设置环境变量,顾名思义。对于不同的用户,$ PATH可能是不同的结果。但是,它可以帮助脚本在无需修改的系统上运行,而这些系统将合理的perl解释器放在了奇怪的地方。
John Mahowald

4

execLinux内核的系统调用#!本身了解shebangs()

当您进行bash操作时:

./something

在Linux上,这会exec使用path 调用系统调用./something

内核的这一行在传递给exec以下文件的文件上调用:https : //github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

它读取文件的第一个字节,并将其与进行比较#!

如果比较正确,那么Linux内核将解析其余的行,该内核exec将使用路径/usr/bin/env python和当前文件作为第一个参数进行另一个调用:

/usr/bin/env python /path/to/script.py

这适用于任何#用作注释字符的脚本语言。

是的,您可以使用以下方法进行无限循环:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash识别错误:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! 只是碰巧是人类可读的,但这不是必需的。

如果文件以不同的字节开头,则exec系统调用将使用其他处理程序。另一个最重要的内置处理程序用于ELF可执行文件:https : //github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305用于检查字节7f 45 4c 46(也恰好是人类的)可读.ELF)。让我们通过读取的前4个字节(/bin/ls是ELF可执行文件)来确认这一点:

head -c 4 "$(which ls)" | hd 

输出:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

因此,当内核看到这些字节时,它将获取ELF文件,将其正确地放入内存,并使用它开始一个新进程。另请参阅:https : //stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

最后,您可以使用该binfmt_misc机制添加自己的shebang处理程序。例如,您可以.jarfiles添加自定义处理程序。该机制甚至通过文件扩展名支持处理程序。另一个应用程序是使用QEMU透明地运行不同体系结构的可执行文件

我不认为POSIX指定shebang,但是:https ://unix.stackexchange.com/a/346214/32558,尽管它确实在基本原理部分以“如果系统支持可执行脚本,可能发生”。


1
./something从shell 运行不会将完整路径传递到exec,而是将输入的路径准确传递。您可以在答案中更正吗?不要echo "$0"在你的脚本,你会看到这种情况。
AndiDog '18

2

实际上,如果您采取这种做法,则在shebang行中指出的可执行文件只是一个可执行文件。将某些文本解释器用作可执行文件是有意义的,但不是必须的。只是为了澄清和演示,我做了一个相当无用的测试:

#!/bin/cat
useless text
more useless text
still more useless text

将文件命名为test.txt并设置可执行位chmod u+x test.txt,然后“调用”它:./test.txt。如预期的那样,将输出文件的内容。在这种情况下,cat不会忽略shebang线。它仅输出所有行。因此,任何有用的口译员都应该能够忽略该提示行。对于bash,perl和PHP,它只是一条注释行。所以是的,这些忽略了shebang线。


-1

根据我的收集,只要文件具有可执行位设置并被调用,内核就会分析文件头,以确定如何进行处理(据我所知,您可以通过LKM为自定义文件格式添加自定义处理程序)。如果文件似乎是带有#!的文本文件!组合开始时,将其执行分派到另一个可执行文件(通常是各种shell),该路径将在所述shebang之后的同一行中直接指定。然后内核继续执行外壳程序并传递文件以供处理。

简而言之,使用哪个shell调用脚本都没有关系-内核会将执行分派到相应的一种方式。


4
bash ./myscript.sh和之间有明显的区别./myscript.sh
CVn

您所说的“明显差异”是什么意思?
jrara

3
@jrara看到我的回答,“使用哪个shell调用脚本都没有关系”的说法根本就不正确。
Thomas Nyman
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.