#! 到底做了什么

2025-06-09

#! 到底做了什么

当我们运行以 (又名 shebang )开头的文件时究竟会发生什么#!,以及为什么有些人使用#!/usr/bin/env bash

#!工作原理

shebang#!用于告诉内核应该使用哪个解释器来运行文件中的命令。

当我们运行一个以 开头的文件时#!,内核会打开该文件,并获取从 开始#!到行末的内容。为了便于教学,我们假设它将command从 shebang 开始到 行末结束的内容保存在一个名为 的字符串变量中。

在此之后,内核尝试运行一个命令,其中包含内容command,并将我们要执行的文件的文件名作为第一个参数。

myscript.sh因此,如果您有一个用一些 shell 命令调用的以 开头的可执行文件#!/bin/bash,则当您运行它时,内核将执行/bin/bash myscript.sh

在下面的例子中,您将会非常清楚地看到这一点。

从经典开始hello.sh

#!/bin/bash
echo "Hello World!"
Enter fullscreen mode Exit fullscreen mode

假设此文件具有可执行权限,当您在命令行中输入以下内容时:

$ ./hello.sh
Enter fullscreen mode Exit fullscreen mode

内核会注意到#!第一行中的 ,然后获取其后的内容,在本例中为/bin/bash。然后执行的操作具有与此完全相同的效果:

$ /bin/bash hello.sh
Enter fullscreen mode Exit fullscreen mode

让我们使用另一个示例#!/bin/cat。文件名为shebangcat

#!/bin/cat
All the contents of this file will be
printed in the screen when it's executed
(including the '#!/bin/cat' in the first line).
Enter fullscreen mode Exit fullscreen mode

让我们记住:

  • 事情的后续是:/bin/cat
  • 文件名:./shebangcat

因此执行的操作如下:/bin/cat ./shebangcat

亲自看看吧:

$ ./shebangcat
#!/bin/cat
All the contents of this file will be
printed in the screen when it's executed
(including the '#!/bin/cat' in the first line).
Enter fullscreen mode Exit fullscreen mode

为了更清楚地说明我的意思,我们再举一个例子。以下文件名为shebangecho

#!/usr/bin/echo
The contents of this file will *NOT* be
printed when it's executed.
Enter fullscreen mode Exit fullscreen mode

让我们检查一下:

$ ./shebangecho
./shebangecho
Enter fullscreen mode Exit fullscreen mode

输出是文件的名称,因为这是内核执行的内容/usr/bin/echo ./shebangecho

另一件有趣的事情是,如果我们在调用脚本时传递参数,这些参数也会传递给内核执行的命令。正如我们在下面的例子中看到的shebangls.sh

#!/bin/ls
The contents here doesn't matter.
Enter fullscreen mode Exit fullscreen mode

现在,当我们运行它时:

$ ./shebangls.sh
./shebangls.sh

$ ./shebangls.sh -l
-rwxr-xr-x 1 meleu meleu 41 Nov 28 14:42 ./shebangls.sh

$ ./shebangls.sh notfound
/bin/ls: cannot access 'notfound': No such file or directory
./shebangls.sh
Enter fullscreen mode Exit fullscreen mode

为什么有些人使用#!/usr/bin/env

你可能见过一些脚本以 开头,#!/usr/bin/env bash而你习惯于只看到#!/bin/bash。这样做的原因是为了提高脚本的可移植性(尽管这是一个有争议的问题,正如我们将在下面看到的)。

如果该env命令不带参数,则会打印出一个包含所有环境变量的(大)列表。但如果env该命令后跟一个命令,则会在另一个 Shell 实例中运行该命令。

🤔 -好的,但是这会如何影响可移植性?!

当您使用 时,#!/bin/bash您显然是在表示bash位于/bin/目录中。这似乎是所有 Linux 发行版的默认设置,但在其他 Unix 版本中,这种情况可能不会发生(例如bash可以放在 中/usr/bin/)。在这样的系统中,以 开头的脚本#!/bin/bash会导致bad interpreter: No such file or directory

当你运行 时env bash,会在变量env搜索,然后运行找到的第一个变量。通常在 中,但在其他系统上运行脚本的用户可以将其放在 中,甚至可以在 中测试其他版本bash$PATHbash/bin//usr/bin//home/user/bin/bash

因此,为了使该脚本具有更大的普及范围并在 Linux 以外的环境中使用,有些人建议使用该env技术。

🤔 -可是等等!怎么保证 会env一直在 呢/usr/bin/

没有任何保证...😇

此建议基于 Unix 系统中常见的情况。我看到/usr/bin/env一些现代项目(例如RetroPie)中也使用了它,但它特别适用于运行 Python 甚至 NodeJS 脚本的情况。

让我们以这个 NodeJS 用法为例。我想通过调用脚本的文件名来调用一个 NodeJS 脚本。那么我可以这样做:

#!/usr/bin/node
console.log('Hello World from NodeJS');
Enter fullscreen mode Exit fullscreen mode

问题是我通常通过Node 版本管理器安装 Node ,而不是使用发行版的包管理器。所以我的node版本是这样的:

$ which node
/home/meleu/.nvm/versions/node/v14.15.1/bin/node
Enter fullscreen mode Exit fullscreen mode

无论如何我都想把它写进#!/home/meleu/.nvm/versions/node/v14.15.1/bin/node我的剧本里!

因此,这里的解决方案是使用#!/usr/bin/env node

#!如果我根本不想使用怎么办?

我强烈建议您不要编写或运行没有 shebang 的 shell 脚本#!

正如我们所说,Shebang 会告诉内核使用哪个解释器来运行文件中的命令。如果你运行脚本时没有指定解释器,shell 会生成另一个实例并尝试运行脚本中的命令。这意味着它将执行文件中找到的所有命令,即使该文件是用 zsh、ksh、dash、fish、node、python 或其他语言编写的。

总结:脚本的开头一定要用#!shebang。最好用#!/usr/bin/env

链接

鏂囩珷鏉yu簮锛�https://dev.to/meleu/what-the-shebang-really-does-and-why-it-s-so-important-in-your-shell-scripts-2755
PREV
有关位置属性的所有提示,可避免常见错误
NEXT
我通过 Scrimba 重新学习了 JavaScript