为什么需要在脚本文件开头添加 #!/bin/bash?

659
我之前制作过Bash脚本,并且它们都在没有#!/bin/bash的情况下正常运行。把它放进去有什么意义呢?会有任何不同吗?
此外,如何发音#?我知道!的发音是“bang”。 #!怎么发音?

16
除非你别无选择,否则不需要也不应该这样做。在你能使用的时候,请使用“#!/bin/sh”,并学习一下(POSIX) shell和bash之间的区别。在你的简历变得太长之前,总有一天你会发现自己在一个不同的系统上,而你仍然希望你的脚本能够正常工作。 - Jens
79
发音为“Hash-Bang”或者“She-Bang”。 - Beachhouse
32
值得注意的是,这个操作仅在将脚本作为可执行文件运行时才会执行。因此,如果你设置了可执行标志,然后输入 ./yourscript.extension(例如 ./helloworld.py./helloworld.sh),它将查找顶部的解释器,即 #!/bin/python!#/bin/bash,而当像 python helloworld.py 这样执行脚本时,第一行将不被观察,因为它被注释掉了。所以这是 shell/kernel 的一个特殊序列。 - Jacqlyn
@JFA,当使用#!来表示Python和#!/bin/bash来表示Bash时,它们之间的顺序是否有变化? - AAI
2
@AjeyaAnand 不,那是打错了,你发现得好。 - Jacqlyn
显示剩余2条评论
10个回答

539

这是一种约定,让*nix shell知道要运行哪种解释器。

例如,旧版的ATT默认使用Bourne shell(sh),而旧版的BSD默认使用C shell(csh)。

即使在今天(大多数系统运行bash,“Bourne Again Shell”),脚本也可以是bash、python、perl、ruby、PHP等等。例如,你可能会看到#!/bin/perl#!/bin/perl5

PS: 感叹号(!)亲切地被称为“bang”。Shell注释符号(#)有时被称为“hash”。

PPS: 请记住,在*nix下,将后缀与文件类型关联仅仅是一种惯例,而不是一条“规则”。可执行文件可以是二进制程序,也可以是数百万种脚本类型和其他东西。因此需要使用#!/bin/bash


1
我发现了另一个有用的东西,$#。那叫什么? - node ninja
113
shebang并不是shell的惯例,而是在处理execve(2)系统调用时由kernel解释的。因此,shebang是kernel的惯例,而不是shell的惯例。 - Basile Starynkevitch
12
同时,这有助于一些编辑器(如Vim)确定语言并进行语法高亮,如果文件没有扩展名的话。如果没有shebang,Vim将把Bash脚本显示为纯文本文件。 - Aaron Blenkush
3
我看到了很多针对这个问题的答案,但是你回答的最后一部分“可执行程序可以是二进制程序,任何一种脚本类型和其他的东西。因此需要 #!/bin/bash." 真的说中了重点。 - senseiwu
7
那么... _hash-bang-slash-bin-slash-bash_? 意思为:那么... #!/bin/bash? - Bernat
显示剩余10条评论

161
为了更加准确,当shebang#!出现在可执行文件(xmode)的前两个字节时,它会被execve(2)系统调用解释(该调用执行程序)。但是POSIX规范中的execve没有提及shebang。
它必须后跟解释器可执行文件的文件路径(顺便说一下,这个路径甚至可以是相对路径,但通常是绝对路径)。

一个好用的技巧(或者也许是不太好用的)来查找用户 $PATH 中的解释器(例如python)是使用 env 程序(在所有 Linux 上始终位于 /usr/bin/env),如下所示。

 #!/usr/bin/env python

任何ELF可执行文件都可以成为解释器。你甚至可以使用#!/bin/cat或者#!/bin/true(但这通常是没有用的)。

9
有关#!/usr/bin/env技巧的讨论,请参见此问题 - Keith Thompson
如果我想向Python传递一个参数,我该怎么做?实际上,我想执行#!/usr/bin/env bash -x。我该怎么做? - indianwebdevil
很简单,我自己找到了,只需在 #!/usr/bin/env bash -x 后添加参数即可。 - indianwebdevil
bash is almost always at /bin/bash so your shebang should be #!/bin/bash -x - Basile Starynkevitch

55

这个被称为 shebang。在Unix语言中,#号被称为sharp(尖音符),或者hash(如Twitter上的标签),!号被称为bang。你实际上可以通过两个感叹号(!!),也就是bang-bang来引用之前的shell命令。因此,当它们结合在一起时,你得到哈希-叹号(hash-bang),或者 shebang。

#!后面的部分告诉Unix要使用哪个程序来运行它。如果没有指定,它将尝试使用bash(或sh、zsh,或任何你的$SHELL变量值是什么的程序),但是如果指定了,它将使用该程序。此外,在大多数语言中,#是注释符号,所以这一行在后续执行中将被忽略。


2
如果我已经在bash中,那么如果它看到#!/bin/bash,它会启动另一个bash实例吗?如果我已经在bash中并且省略它,有什么区别吗? - node ninja
2
@javascriptninja 无论哪种方式,它都将启动一个新的bash shell。在bash的情况下,只要您已经在使用bash,实际上没有任何区别。只有当(a)您需要在不仅仅是shell的东西中运行,比如python或perl,或者(b)您没有使用bash shell(即您使用zsh),但需要运行需要在bash中运行的东西时,shebang才真正重要。 - austin1howard
然而,在我看来,包含shebang是一个好的实践,这样阅读代码的人就知道正在发生什么。 - austin1howard
2
错误:execve(2)系统调用不使用$SHELL变量。解释shebang的是内核。 - Basile Starynkevitch
1
@BasileStarynkevitch 是正确的,内核中的 ELF 加载器解释了 shebang。我是在说如果没有提供 shebang,那么将使用 $SHELL。 - austin1howard
显示剩余2条评论

22

每个发行版都有一个默认的shell。Bash是大多数系统上的默认值。如果你碰巧在一个具有不同默认shell的系统上工作,则如果脚本是针对Bash编写的,它们可能无法按预期工作。

Bash随着时间的推移已经发展起来了,从ksh和sh中获取代码。

在您的脚本的第一行添加#!/bin/bash,告诉操作系统调用指定的shell来执行接下来的命令。

#!通常被称为“哈希-惊叹号”,“she-bang”或“sha-bang”。


20
操作系统会使用默认的 shell 来运行你的 shell 脚本。因此,在脚本开头提供 shell 路径,就是在要求操作系统使用该特定的 shell。这对于可移植性也非常有用。

20

Shebang是一种指令,告诉加载器在尝试执行文件时使用在#!后指定的程序作为解释器。因此,如果您尝试运行名为foo.sh的文件,并且该文件顶部有#!/bin/bash,则实际运行的命令是/bin/bash foo.sh。这是一种灵活使用不同解释器处理不同程序的方式。这是在系统级别实现的,用户级别的API是shebang约定。

值得知道的是,shebang是魔数之一,它能够以人类可读的方式识别文件作为给定解释器的脚本。

您提到即使没有shebang,“工作”也是因为相关程序是针对您正在使用的相同shell编写的shell脚本。例如,您可以编写JavaScript文件,然后加上#! /usr/bin/js(或类似内容)来将其作为JavaScript“Shell脚本”使用。


12

这被称为shebang。它由一个井号和一个感叹号字符(#!)组成,其后跟解释器的完整路径,例如/bin/bash。在UNIX和Linux下,所有脚本都使用在第一行指定的解释器来执行。


4

Bash标准是Linux中众多可用shell中的一种。

Shell是一个命令行解释器,接受并运行命令。

Bash通常是大多数Linux发行版中的默认shell。这就是为什么bash与shell是同义词。

Shell脚本通常具有基本相同的语法,但有时也会有所不同。例如,Zsh中的数组索引从1开始,而在bash中则从0开始。如果一个脚本是为Zsh shell编写的,并且包含数组,则在bash中无法正常工作。

为了避免出现不愉快的意外,您应该告诉解释器,您的shell脚本是为bash shell编写的。怎么做呢?

只需在您的bash脚本开始处添加 #!/bin/bash。


1

0

对于使用没有该库的不同系统的人来说,这可能非常有用。如果没有声明并且您的脚本中有一些该系统不支持的函数,则应该声明#/bin/bash。我曾经在工作中遇到过这个问题,现在我只是将其包含为一种惯例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接