在bash
或中sh
,我猜任何以开头的东西#
都是评论。
但在bash
脚本中我们写道:
#!/bin/bash
在 Python 脚本中,有:
#!/bin/python
这是否意味着#
本身就是一条评论而#!
不是?
答案1
该#!
线路用于前脚本运行,然后被忽略什么时候脚本运行。
你问的是舍邦线以及一条普通的评论。
以 开头的行#!
与以 开头的任何其他行一样,都是注释。如果是文件的第一行,或者其他任何地方,#
情况都是如此。#!
#!/bin/sh
有效果, 但解释器本身不读取它。
#
并不是所有编程语言中的注释,但如您所知,它是 Bourne 风格 shell 中的注释,包括sh
和bash
(以及大多数非 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
应该运行脚本是 中第一个出现的脚本PATH
。hello.py
从#!/usr/bin/env python
make./hello.py
run开始/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
它以交互方式接受用户输入,逐行回显给用户,并将其附加到“脚本”文件的末尾。
有用吗? 不太有用。 从概念上来说有趣吗? 完全有趣! 是的。(有点。)
概念上相似的编程/脚本概念(只是为了好玩)
脚本/程序同时使用多种语言, 例如,在没有 hashbang 功能的操作系统中模拟该功能。
(这些程序被称为通晓多种语言的人,但不要将其与另一种意义多语种在软件开发中,一个程序/项目,其中的不同部分用不同的语言编写。
元命令在 QBasic/QuickBASIC 中,它向编译器(用于编译代码)发出代码生成选项信号,但它们是注释的一部分,因此在实际编译/解释过程中被忽略。
答案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
。
内核的这一行在传递给的文件上被调用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
最后,您可以使用该机制添加自己的 shebang 处理程序binfmt_misc
。例如,您可以添加一个.jar
文件的自定义处理程序。此机制甚至支持按文件扩展名处理程序。另一个应用是使用 QEMU 透明地运行不同架构的可执行文件。
然而我不认为 POSIX 指定了 shebang:https://unix.stackexchange.com/a/346214/32558,尽管它确实在基本原理部分中提到过,并且形式为“如果系统支持可执行脚本,则可能会发生某些事情”。