Answers:
#!
使用行之前是运行脚本,然后忽略时运行该脚本。您要问的是,Shebang行和普通评论之间有什么区别。
以开头的行与以#!
开头的任何其他行一样多#
。如果#!
是文件的第一行或其他任何地方,则为true 。#!/bin/sh
可以起作用,但是解释器本身不会读取它。
#
不是所有编程语言中的注释,而是众所周知,它是在Bourne样式的shell中的注释,包括sh
和bash
(以及大多数非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行,而扩展名可能会确定运行脚本的内容。(这并不意味着您需要给脚本扩展名,但这是为什么这样做应该是正确的原因之一)。
#!
之所以被选择用于此目的,恰恰是因为 #
开始发表评论。该#!
行用于系统,而不是解释器,解释器应忽略该行。
你(原)说你使用#!/bin/sh
的bash
脚本。只有在脚本不需要任何bash
扩展名时,您才应该这样做- sh
需要能够运行脚本。sh
并非始终是的符号链接bash
。通常,包括在所有最近的Debian和Ubuntu系统上,sh
都是的符号链接dash
。
您还说过(在问题的第一个版本中,在编辑之前),您使用开始了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
几乎总是存在于bash
OS附带的系统上,但是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.py
python hello.py
您无法使用的原因#!python
是:
/
)。python
在当前目录中执行。当命令不包含斜杠时搜索路径是特定的shell行为。偶尔一个Python或其他脚本不是一个shell脚本将有一个shebang行开始#!/bin/sh ...
在那里...
是一些其他的代码。有时这是正确的,因为有一些方法可以sh
使用带有参数的Bourne兼容shell()来使其调用Python解释器。(其中一个参数可能包含python
。)但是,在大多数情况下,#!/usr/bin/env python
它更简单,更优雅,并且更有可能按照您想要的方式工作。
许多编程和脚本语言以及其他一些文件格式都#
用作注释。对于其中的任何一种,可以通过一个程序运行该语言的文件,该程序通过在的第一行中指定该程序作为参数来将其作为参数#!
。
在某些编程语言中,#
通常不是注释,但作为特殊情况,如果第一行以开头,则将其忽略#!
。#!
即使#
没有在行中添加注释,这也有助于语法的使用。
虽然不太直观,但是文件格式可以容纳第一行并#!
以可执行文件的完整路径开头的任何文件都可以带有shebang行。如果执行此操作,并且文件被标记为可执行文件,则可以像运行程序一样运行它...使其像文档一样打开。
某些应用程序有意使用此行为。例如,在VMware中,.vmx
文件定义虚拟机。您可以像运行脚本一样“运行”虚拟机,因为这些文件被标记为可执行文件,并且有一行框,导致它们在VMware实用程序中打开。
rm
删除文件。它不是脚本语言。但是,#!/bin/rm
可以运行已启动并标记为可执行的文件,并在运行该文件时对其rm
进行调用并删除它。
这通常被概念化为“文件删除自身”。但是该文件实际上并没有运行。这更类似于上述.vmx
文件情况。
尽管如此,由于该#!
行有助于执行简单命令(包括命令行参数),因此您可以通过这种方式执行一些脚本。作为比复杂得多的“脚本”的简单示例#!/bin/rm
,请考虑:
#!/usr/bin/env tee -a
这将以交互方式获取用户输入,并将其逐行回显给用户,并将其附加到“脚本”文件的末尾。
有用?不是特别的。概念上有趣吗?完全!是。(有些。)
一次是多种语言的脚本/程序,例如,在没有它的OS中模拟hashbang功能。
(这些程序称为polyglots,但这不要与软件开发中的其他polyglot含义混淆,该软件 /程序是其中不同部分用不同语言编写的程序/项目。)
元命令在QBasic中/ QuickBASIC中,这标志着编译器(用于编译的代码)的代码生成选项,但均的评论部分和实际编译/口译过程中是忽略不计。
-x
标志做什么?
-x
“跳过[S]的第一线......” 2号线被编号1
代替2
,3号线2
替代3
,等等。这就是为什么你不应该使用该标志。;)-x
用于在非类Unix操作系统上的脚本,这些操作系统的类shebang语法并非以开头#
(因此不是Python注释)。
perl script.pl
vs. ./script.pl
),则解释器将读取shebang行以解析诸如的标志-w
。但是不建议您依赖此功能。
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上的用户内容相同,因此,允许注明出处。
#!
被称为shebang
当它发生时作为上的一个脚本的初始行初始两个字符。在脚本中使用它来指示要执行的解释器。的shebang
是为操作系统(内核),而不是为壳; 因此它不会被解释为评论。
礼貌: http : //en.wikipedia.org/wiki/Shebang_%28Unix%29
通常,如果文件是可执行文件,但实际上不是可执行文件(二进制),并且存在此类行,则在#!之后指定该程序。以脚本名称及其所有参数开头。这两个字符#和!必须是文件中的前两个字节!
详细信息: http : //wiki.bash-hackers.org/scripting/basics#the_shebang
不,它仅由exec
Linux内核的系统调用使用,并由解释器视为注释
当您进行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处理程序。例如,您可以为.jar
files添加自定义处理程序。该机制甚至通过文件扩展名支持处理程序。另一个应用程序是使用QEMU透明地运行不同体系结构的可执行文件。
我不认为POSIX会指定shebangs:https ://unix.stackexchange.com/a/346214/32558 ,尽管它在基本原理部分中确实提到,并且形式为“如果系统支持可执行脚本,则可能发生”。
#include
。那里也不#
是要发表评论。