shebang
核心结论:shebang 不是 Bash 的语法,也不是某门编程语言的特性,是 Unix-like 操作系统内核的机制(magic number,魔数。内核
execve()系统调用读取文件头两个字节0x23 0x21(即#!),拉起指定解释器,与脚本语言本身无关。
机制本质:内核魔数,不是语言语法
#! 位于脚本第一行开头,告诉操作系统用哪个解释器执行脚本。给文件加执行权限后直接 ./demo.sh 运行,内核读取文件开头,看到 #! 就取出后面的路径,启动对应解释器,并把脚本文件作为参数传给它。伪等价逻辑:./etl.py(有 shebang + chmod +x)实际约等于 /usr/bin/env python3 ./etl.py。
这一行对解释器本身来说是注释。Bash、Python、Perl、Ruby 都把 # 当注释开头,读到直接丢弃;脚本语言根本不知道什么叫 shebang——是内核在 execve 时拦截解析,把脚本路径当参数丢给解释器。写了 shebang 也不要求解释器认识 #!,因为内核已经把这一行拿走了。
也因此只有直接执行才生效:
| 调用方式 | 是否读取 shebang |
|---|---|
./etl.py(chmod +x) | ✅ 内核解析 shebang |
python3 etl.py / bash demo.sh | ❌ 内核直接调用解释器,#! 行被当成注释 |
词源
单词 shebang /ʃɪˈbæŋ/,别名 hash-bang、sha-bang、pound-bang。构词来自黑客俚语:# 在 Unix 圈叫 hash,! 叫 bang(源自电传打字机时代,! 触发打印机猛敲一下的”bang”声);hash-bang 口语连读讹变为 shebang,拼写受英语原有旧词干扰固定下来。Dennis Ritchie 本人说过:开发这个内核特性时官方根本没有起名字,shebang 是社区事后叫出来的俗称。
英语原有一个 1860 年代美国内战俚语 shebang(棚屋、摊子),习语 the whole shebang 表示全部、整套。计算机术语与它没有语义继承,纯属拼法巧合的同形异义。中文译名释伴,音译加意译:解释器伴侣。
哪些语言能用
能用 shebang 需要三个前提:Unix-like 系统;存在可执行的解释器,能接收脚本文件作为命令行参数;解释器能容忍或跳过 #! 行。满足就能写:
#!/usr/bin/env bash # shell
#!/usr/bin/env python3 # python
#!/usr/bin/env perl # perl
#!/usr/bin/env ruby # ruby
#!/usr/bin/env node # javascript (NodeJS)
#!/usr/bin/env awk -f # awk 脚本
#!/usr/bin/env lua # lua编译型语言(C/C++/Rust)不能也不需要——它们输出二进制可执行文件,开头是程序自己的魔数而非文本 #!。例外是提供脚本模式的执行器(go run、runhaskell),可以写 shebang 调它们,但不是主流用法。
注释符号不是 # 的语言照样能跑,比如 PHP(注释 //):
#!/usr/bin/env php
<?php
echo "hi";内核照常解析 #! 交给 php 解释器,php 会主动跳过开头这一行——这是解释器为 Unix 做的兼容,不是语言语法。反过来,如果某个解释器不会自动跳过 #! 行,第一行就会被当成代码报错,shebang 便不可用。
Windows 原生内核不支持 shebang。Windows 的 Python 启动器是用户态程序做的模拟兼容,不是操作系统机制。
写法与坑
基本语法 #!/解释器路径 [可选参数]。路径有两种写法:硬编码绝对路径(#!/usr/bin/python3)会因不同机器解释器位置不同而报错;env 写法让 env 去 PATH 里搜索解释器,适配不同机器,一律推荐:
#!/usr/bin/env python3
print("hello")四个高频坑
- 必须是文件第一行,前面不能有空行或空格,否则 shebang 失效。
- Windows 换行符 CRLF:脚本在 Windows 编辑后传到 Linux,第一行变成
#!/usr/bin/env python3\r,\r被当成解释器名,报bad interpreter: No such file or directory。修复:dos2unix script.py。- 直接执行需要
chmod +x;python3 script.py不需要权限,但也无视 shebang。- shebang 后只传一个参数(
#!/bin/bash -e合法);多个参数(-e -u)跨平台行为不一致,不要写。
调度场景(crontab、Airflow)下,ETL 脚本第一行写 shebang,调度系统就能直接 ./etl.py、./etl.sh,不必写死 python3 etl.py;不写也能跑,只是调用方必须显式指定解释器。