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

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

bash或中sh,我猜任何以开头的东西#都是评论

但在bash脚本中我们写道:

#!/bin/bash

在 Python 脚本中,有:

#!/bin/python

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

答案1

#!线路用于脚本运行,然后被忽略什么时候脚本运行。

你问的是舍邦线以及一条普通的评论。

以 开头的行#!与以 开头的任何其他行一样,都是注释。如果是文件的第一行,或者其他任何地方,#情况都是如此。#!#!/bin/sh 有效果, 但解释器本身不读取它

#并不是所有编程语言中的注释,但如您所知,它是 Bourne 风格 shell 中的注释,包括shbash(以及大多数非 Bourne 风格 shell,如csh)。它是Python 中的注释。它是各种配置文件中的注释,这些配置文件实际上根本不是脚本(例如/etc/fstab)。

假设一个 shell 脚本以 开头#!/bin/sh。这是一个注释,解释器 (shell) 会忽略该字符后面的所有内容#

一行的目的#!不是向解释器提供信息。一行的目的#!是告诉操作系统(或启动解释器的任何进程)用什么作为解释器

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

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

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

#!正是为实现这一目的而选择的因为 #开始注释。此#!行是针对系统的,而不是针对解释器的,因此解释器应该忽略它。

Bash 脚本的 Shebang Line

您(最初)说您使用#!/bin/sh脚本bash。您只应在脚本不需要 的任何bash扩展时才这样做sh——需要能够运行脚本。sh并不总是指向 的符号链接bash。通常,包括在所有远程最新的 Debian 和 Ubuntu 系统sh是 的符号链接dash

Python 脚本的 Shebang Line

您还说过(在编辑之前,在问题的第一个版本中),您使用 来启动 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

然而问题在于,虽然 Python/bin/sh应该始终存在,并且/bin/bash几乎总是存在于bash操作系统自带的系统上,但它可能存在于各种各样的地方。

因此,许多 Python 程序员改用这个:

#!/usr/bin/env python

(或者#!/usr/bin/env python3对于 Python 3。)

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

  • env几乎总是位于/usr/bin
  • 在大多数系统上,无论python 应该运行脚本是 中第一个出现的脚本PATHhello.py#!/usr/bin/env pythonmake ./hello.pyrun开始/usr/bin/env python hello.py,它(实际上)相当于运行python hello.py

不能使用的原因#!python是:

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

有时,Python 或其他非 shell 脚本会有一个以#!/bin/sh ...where ...is some other code 开头的 shebang 行。有时这是正确的,因为有一些方法可以调用 Bourne 兼容 shell ( sh) 并带上参数,使其调用 Python 解释器。(其中一个参数可能包含python。)但是,对于大多数目的来说,#!/usr/bin/env python更简单、更优雅,也更有可能按照您想要的方式工作。

其他语言中的 Shebang Lines

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

在某些编程语言中,#通常不是注释,但作为特殊情况,如果第一行以 开头,则会被忽略#!。这方便了语法的使用,#!即使#一行原本不是注释。

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

虽然不太直观,但只要文件格式可以容纳以 开头的第一行,#!后面跟着可执行文件的完整路径,就可以有一个 shebang 行。如果您这样做,并且文件被标记为可执行文件,那么您可以像程序一样运行它……使其像文档一样打开。

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

不作为 Scrips 运行但仍然像脚本一样运行的文件的 Shebang 行

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

这通常被概念化为“文件删除自身”。但文件实际上根本没有运行。这更像是上面描述的.vmx文件的情况。

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

#!/usr/bin/env tee -a

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

有用吗? 不太有用。 从概念上来说有趣吗? 完全有趣! 是的。(有点。)

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

答案2

当 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”作为第一个参数传递。

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

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

来源:本答案的部分内容(略有修改)来自Shebang(Unix)英文維基百科(经过维基百科贡献者)本文是根据 CC-BY-SA 3.0 许可,与 AU 上的用户内容相同,因此允许进行此衍生且注明来源。

答案3

#!shebang当它作为脚本首行的最初两个字符出现时,被称为。它在脚本中用于指示执行的解释器。shebang是用于操作系统(内核)的,而不是用于 shell 的;因此它不会被解释为注释。

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

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

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

答案4

不是,它仅被 Linux 内核的系统调用使用exec,并被解释器视为注释

当你在 bash 上执行如下操作时:

./something

在 Linux 上,这将使用exec路径调用系统调用./something

内核的这一行在传递给的文件上被调用exechttps://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

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

然而我不认为 POSIX 指定了 shebang:https://unix.stackexchange.com/a/346214/32558,尽管它确实在基本原理部分中提到过,并且形式为“如果系统支持可执行脚本,则可能会发生某些事情”。

相关内容