解释器是否读取#!/ bin / sh?


66

bash或中sh,我想任何#注释开头的内容。

但是在bash脚本中,我们这样写:

#!/bin/bash

在Python脚本中,有:

#!/bin/python

这是否意味着它#本身就是评论,而#!不是评论?


1
并且,一旦您开始查看Apparmor的个人资料,就会看到#include。那里也不#是要发表评论。

4
@ vasa1但是在shell脚本开始时,关于hashbang行通常不被理解的关键是它们注释
伊利亚·卡根

Answers:


100

#!使用行之前是运行脚本,然后忽略运行该脚本。

您要问的是,Shebang行和普通评论之间有什么区别。

以开头的行与以#!开头的任何其他行一样多#。如果#!是文件的第一行或其他任何地方,则为true 。#!/bin/sh 可以起作用,但是解释器本身不会读取它

#不是所有编程语言中的注释,而是众所周知,它是在Bourne样式的shell中的注释,包括shbash(以及大多数非Bourne样式的shell,如csh)。这也是Python中的注释。它是各种配置文件中的注释,这些配置文件根本不是真正的脚本(例如/etc/fstab)。

假设Shell脚本以开头#!/bin/sh。那是一条注释,解释器(外壳程序)将忽略#字符后一行上的所有内容。

行的目的#!不是要向解释器提供信息。该#!行的目的是告诉操作系统(或启动解释程序的任何进程)用作解释程序的内容

  • 如果您将脚本作为可执行文件调用,例如通过运行./script.sh,系统将参考第一行以查看是否以开头#!,然后是零个或多个空格,然后是命令。如果是这样,它将以脚本名称作为参数运行该命令。在此示例中,它运行/bin/sh script.sh(或从技术上来说/bin/sh ./script.sh)。

  • 如果通过显式调用解释器来调用脚本,#!则永远不会参考该行。因此,如果您运行sh script.sh,则第一行无效。如果script2.sh的第一行是#!/usr/games/nibbles,则running sh script2.sh不会尝试在中打开脚本nibbles(而是./script2.sh会)。

您会注意到,在两种情况下,脚本的扩展名(.sh)都不会影响脚本的运行方式。在类似Unix的系统中,这通常不会影响脚本的运行方式。在某些其他系统(例如Windows)上,系统#!可能会完全忽略shebang行,而扩展名可能会确定运行脚本的内容。(这并不意味着您需要给脚本扩展名,但这是为什么这样做应该是正确的原因之一)。

#!之所以被选择用于此目的,恰恰是因为 #开始发表评论。该#!行用于系统,而不是解释器,解释器应忽略该行。

Bash脚本的Shebang行

你(原)说你使用#!/bin/shbash脚本。只有在脚本不需要任何bash扩展名时,您才应该这样做- sh需要能够运行脚本。sh并非始终是的符号链接bash。通常,包括在所有最近的Debian和Ubuntu系统上sh都是的符号链接dash

Shebang Line for Python脚本

您还说过(在问题的第一个版本中,在编辑之前),您使用开始了Python脚本#!/bin/sh read by the interpretor。如果从字面上看是那样,那么您绝对应该停止这样做。如果hello.py以该行开头,运行将./hello.py执行:

/bin/sh read by the interpretor hello.py

/bin/sh会尝试执行一个名为read(带有by the interpretor hello.py作为其参数的)脚本,read(希望)找不到,并且Python解释器永远不会看到您的Python脚本。

如果您犯了这个错误但没有遇到我要描述的问题,则可能是通过显式指定解释器(例如python hello.py)来调用Python脚本,从而导致第一行被忽略。当您将脚本分发给其他人或在很长一段时间后使用它们时,可能尚不清楚这对于它们起作用是必要的。最好现在修复它们。或者至少完全删除第一行,这样当它们无法运行并./显示错误消息时才有意义。

对于Python脚本,如果您知道Python解释器在哪里(或将要在哪里),则可以使用#!以下方式编写该行:

#!/usr/bin/python

或者,如果它是Python 3脚本,则应指定python3,因为python几乎总是Python 2

#!/usr/bin/python3

但是,问题在于,尽管/bin/sh应该一直存在,并且/bin/bash几乎总是存在于bashOS附带的系统上,但是Python可能存在于许多地方。

因此,许多Python程序员改用以下代码:

#!/usr/bin/env python

(或#!/usr/bin/env python3适用于Python3。)

这使得脚本依赖于env在“正确的位置”,而不是依靠在python正确的位置。这是一件好事,因为:

  • env几乎总是位于/usr/bin
  • 在大多数系统上,无论哪个运行脚本python 都应是中最先出现的脚本PATH。从make run 开始hello.py,(实际上)等价于running 。#!/usr/bin/env python./hello.py/usr/bin/env python hello.pypython hello.py

您无法使用的原因#!python是:

  • 您希望指定的解释器由绝对路径给出(即,以开头/)。
  • 调用过程将python 在当前目录中执行。当命令不包含斜杠时搜索路径是特定的shell行为。

偶尔一个Python或其他脚本不是一个shell脚本将有一个shebang行开始#!/bin/sh ...在那里...是一些其他的代码。有时这是正确的,因为有一些方法可以sh使用带有参数的Bourne兼容shell()来使其调用Python解释器。(其中一个参数可能包含python。)但是,在大多数情况下,#!/usr/bin/env python它更简单,更优雅,并且更有可能按照您想要的方式工作。

舍邦行用其他语言

许多编程和脚本语言以及其他一些文件格式都#用作注释。对于其中的任何一种,可以通过一个程序运行该语言的文件,该程序通过在的第一行中指定该程序作为参数来将其作为参数#!

在某些编程语言中,#通常不是注释,但作为特殊情况,如果第一行以开头,则将其忽略#!#!即使#没有在行中添加注释,这也有助于语法的使用。

Shebang行用于不作为脚本运行的文件

虽然不太直观,但是文件格式可以容纳第一行并#!以可执行文件的完整路径开头的任何文件都可以带有shebang行。如果执行此操作,并且文件被标记为可执行文件,则可以像运行程序一样运行它...使其像文档一样打开。

某些应用程序有意使用此行为。例如,在VMware中,.vmx文件定义虚拟机。您可以像运行脚本一样“运行”虚拟机,因为这些文件被标记为可执行文件,并且有一行框,导致它们在VMware实用程序中打开。

Shebang行中的文件不是作为脚本运行,而是像脚本一样运行

rm删除文件。它不是脚本语言。但是,#!/bin/rm可以运行已启动并标记为可执行的文件,并在运行该文件时对其rm进行调用并删除它。

这通常被概念化为“文件删除自身”。但是该文件实际上并没有运行。这更类似于上述.vmx文件情况。

尽管如此,由于该#!行有助于执行简单命令(包括命令行参数),因此您可以通过这种方式执行一些脚本。作为比复杂得多的“脚本”的简单示例#!/bin/rm,请考虑:

#!/usr/bin/env tee -a

这将以交互方式获取用户输入,并将其逐行回显给用户,并将其附加到“脚本”文件的末尾。

有用?不是特别的。概念上有趣吗?完全!是。(有些。)

概念上类似的编程/脚本概念(只是为了好玩)


@Rinzwind Thx!(顺便说一句,这个答案不是来自其他地方,如果您想知道的话。)
Eliah Kagan

@Rinzwind不用担心,一小时后有8票赞成票,它有可能进一步提高:-)
guntbert

1
如果始终被忽略,那么Pythons -x标志做什么?
gerrit

4
@gerrit好问题。在解释器/编译器报告带有行号的消息的任何语言中,注释的内容都将被忽略,但注释行仍会被计数。在代码行之前添加注释或空白行仍会导致该代码行的行号增加。-x“跳过[S]的第一线......” 2号线被编号1代替2,3号线2替代3,等等。这就是为什么你不应该使用该标志。;)-x用于在非类Unix操作系统上的脚本,这些操作系统的类shebang语法并非以开头#(因此不是Python注释)。
伊莱亚·卡根

4
在Perl中,如果直接启动解释器(perl script.plvs. ./script.pl),则解释器读取shebang行以解析诸如的标志-w。但是不建议您依赖此功能。
OrangeDog

7

shebang是一个字符序列,由字符数字符号和感叹号(例如“#!”)组成,当它出现在脚本的第一行中时是前两个字符。

在* nix操作系统下,运行以shebang开头的脚本时,程序加载器会将脚本的其余首行解析为解释器指令;而是运行指定的解释器程序,将尝试运行脚本时最初使用的路径作为参数传递给它。例如,如果脚本的命名路径为“ path / to / your-script”,则该脚本以以下行开头:

#!/bin/sh

然后指示程序加载器运行程序“ / bin / sh”,而不是运行Bourne shell或兼容的shell,并传递“ path / to / your-script”作为第一个参数。

因此,它以路径“ path / to / python-script”命名脚本,并以以下行开头:

#!/bin/python

然后指示加载的程序运行程序“ / bin / python”而不是例如Python解释器,并传递“ path / to / python-script”作为第一个参数。

简而言之,“#”将注释掉一行,而字符序列“#!” 出现在脚本首行的前两个字符的含义如上所述。

有关详细信息,请参见为什么某些脚本以#开头!...?

来源:此答案的某些部分(略有修改)来自英语维基百科(由Wikipedia贡献者)上的Shebang(Unix)。本文是根据CC-BY-SA 3.0许可的,与AU上的用户内容相同,因此,允许注明出处。


4

#!被称为shebang当它发生时作为上的一个脚本的初始行初始两个字符。在脚本中使用它来指示要执行的解释器。的shebang是为操作系统(内核),而不是为壳; 因此它不会被解释为评论。

礼貌: http : //en.wikipedia.org/wiki/Shebang_%28Unix%29

通常,如果文件是可执行文件,但实际上不是可执行文件(二进制),并且存在此类行,则在#!之后指定该程序。以脚本名称及其所有参数开头。这两个字符#和!必须是文件中的前两个字节!

详细信息: http : //wiki.bash-hackers.org/scripting/basics#the_shebang


0

不,它仅由execLinux内核的系统调用使用,并由解释器视为注释

当您进行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会指定shebangs:https ://unix.stackexchange.com/a/346214/32558 ,尽管它在基本原理部分中确实提到,并且形式为“如果系统支持可执行脚本,则可能发生”。

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.