注:本文为 “Linux | Bash Shebang” 相关合辑。
英文引文,机翻未校。
如有内容异常,请看原文。
Understanding Unix Shebang: History & Implementation
理解 Unix Shebang:历史与实现
Nov 20, 2025 by Alex Johnson
Introduction to the Shebang Mechanism
Shebang 机制简介
Dive into the intricate world of the Unix shebang (#!) mechanism, a cornerstone of script execution in Unix-like operating systems. This comprehensive exploration will take you on a journey from its historical roots at Bell Labs to its diverse implementations across a multitude of systems, including early BSD, Linux, and contemporary macOS. For engineers and developers seeking a fundamental understanding of how their shell scripts are executed, this article serves as an invaluable resource, illuminating the subtle yet critical nuances that govern script behavior.
深入 Unix shebang(#!)机制的复杂世界——这一机制是类 Unix 操作系统中脚本执行的核心基础。本综述将带您追溯其源自贝尔实验室(Bell Labs)的历史根源,探索其在众多系统中的多样化实现,包括早期的 BSD、Linux 以及现代的 macOS。对于希望深入理解 shell 脚本执行原理的工程师和开发人员而言,本文是一份宝贵资源,将阐明支配脚本行为的那些细微却关键的细节。
Origins and Evolution of the Shebang
Shebang 的起源与演变
The story of the shebang begins in the hallowed halls of Bell Labs, the birthplace of Unix. It was conceived as a way to allow scripts to be executed as if they were binary executables, a revolutionary idea at the time. The #! sequence, a seemingly cryptic combination of characters, was chosen to denote the interpreter that should be used to execute the script. This simple yet powerful mechanism paved the way for a new era of scripting, enabling developers to write scripts in various languages and execute them seamlessly within the Unix environment. The initial implementations were fairly basic, but as Unix evolved, so did the shebang mechanism, adapting to the changing needs of the operating system and its users.
Shebang 的故事始于 Unix 的诞生地——神圣的贝尔实验室。它的设计初衷是让脚本能够像二进制可执行文件一样被执行,这在当时是一项具有革命性的理念。#! 这一表面上晦涩的字符组合,被选定用于指定执行脚本所需的解释器。这一简单却强大的机制为脚本编程的新时代奠定了基础,让开发人员能够使用多种语言编写脚本,并在 Unix 环境中无缝执行。早期的实现相对基础,但随着 Unix 的发展,shebang 机制也不断演进,以适应操作系统及其用户不断变化的需求。
Core Functionality
核心功能
At its core, the shebang mechanism is a directive to the operating system, instructing it on how to execute a script. When a script file begins with #!, the operating system parses the line following these characters to determine the interpreter. For example, a script starting with #!/bin/bash tells the system to use the Bash shell to execute the script. This allows users to write scripts in different languages, such as Python (#!/usr/bin/python3) or Perl (#!/usr/bin/perl), and execute them without explicitly invoking the interpreter. The beauty of the shebang lies in its simplicity and flexibility, making it an indispensable tool for system administrators, developers, and anyone who works with Unix-like systems.
本质上,shebang 机制是向操作系统发出的指令,告知系统如何执行脚本。当脚本文件以 #! 开头时,操作系统会解析该字符后的行,以确定对应的解释器。例如,以 #!/bin/bash 开头的脚本会告知系统使用 Bash 外壳执行该脚本。这使得用户能够使用不同的语言(如 Python:#!/usr/bin/python3 或 Perl:#!/usr/bin/perl)编写脚本,且无需显式调用解释器即可执行。shebang 的精妙之处在于其简洁性和灵活性,使其成为系统管理员、开发人员以及所有使用类 Unix 系统的人员不可或缺的工具。
The Role of the Kernel
内核的作用
The kernel plays a central role in the execution of scripts that use the shebang mechanism. When the kernel encounters a script with a shebang, it doesn’t directly execute the script’s contents. Instead, it parses the shebang line to identify the interpreter specified. The kernel then executes the interpreter, passing the script’s path as an argument. This indirection is crucial because it allows the interpreter to handle the script’s execution, including parsing, compiling (if necessary), and running the code. This process ensures that the script is executed in the correct environment and with the appropriate permissions. The kernel’s involvement is a seamless operation, transparent to the user, but it’s the foundation upon which the shebang mechanism operates.
内核在使用 shebang 机制的脚本执行过程中扮演着核心角色。当内核遇到带有 shebang 的脚本时,并不会直接执行脚本内容。相反,它会解析 shebang 行,以识别指定的解释器。随后,内核会执行该解释器,并将脚本的路径作为参数传入。这种间接执行的方式至关重要,因为它让解释器能够处理脚本的执行过程,包括解析、编译(如需)和运行代码。该过程确保脚本在正确的环境中,并以适当的权限执行。内核的参与是一个无缝过程,对用户透明,但它是 shebang 机制运行的基础。
Implementation Across Systems
跨系统实现
One of the most fascinating aspects of the shebang mechanism is its varied implementations across different Unix-like systems. While the basic principle remains the same, subtle but significant differences exist in how various operating systems handle argument passing, maximum line length, security considerations, and the use of utilities like /usr/bin/env. These variations can lead to unexpected behavior if not fully understood, making a deep dive into these implementation details crucial for developers aiming for cross-platform compatibility.
shebang 机制最引人入胜的特点之一是其在不同类 Unix 系统中的多样化实现。尽管基本原理保持一致,但不同操作系统在参数传递、最大行长度限制、安全考量以及 /usr/bin/env 等工具的使用方面,存在着细微但重要的差异。如果未能充分理解这些差异,可能会导致意外行为,因此对于追求跨平台兼容性的开发人员而言,深入研究这些实现细节至关重要。
Argument Passing Variations
参数传递差异
One key area of divergence is how arguments are passed to the interpreter. Some systems pass the entire shebang line as a single argument, while others split the line into multiple arguments. This difference can significantly impact how a script interprets command-line arguments. For instance, a script might expect arguments to be passed individually, but if the system passes the entire line as one argument, the script will likely fail. Understanding these nuances is essential for writing scripts that behave consistently across different platforms. Different approaches to argument parsing can be seen across systems like BSD, Linux, and macOS, each with its own quirks and historical reasons for the chosen method.
一个关键的差异点在于参数如何传递给解释器。有些系统将整个 shebang 行作为单个参数传递,而另一些系统则将该行拆分为多个参数。这种差异会极大地影响脚本对命令行参数的解析方式。例如,某个脚本可能期望参数被单独传递,但如果系统将整行作为一个参数传递,该脚本很可能会执行失败。理解这些细节对于编写在不同平台上行为一致的脚本至关重要。在 BSD、Linux 和 macOS 等系统中,可以看到不同的参数解析方式,每种方式都有其独特的特性和选择该方式的历史原因。
Maximum Line Length Limitations
最大行长度限制
The maximum length of the shebang line also varies across systems. Some systems impose a strict limit, while others allow for longer lines. This limitation can affect the complexity of the interpreter path and any additional arguments passed in the shebang line. If the line exceeds the maximum allowed length, the script may fail to execute, or the system might truncate the line, leading to unexpected behavior. Developers need to be aware of these limitations, especially when writing scripts intended to run on older or more restrictive systems. This limitation is often a relic of older kernel implementations and can sometimes be a point of frustration when dealing with complex script setups.
shebang 行的最大长度在不同系统中也存在差异。有些系统设有严格限制,而另一些系统则允许更长的行。这一限制会影响解释器路径的复杂度以及在 shebang 行中传递的任何额外参数。如果该行超过允许的最大长度,脚本可能无法执行,或者系统可能会截断该行,导致意外行为。开发人员需要了解这些限制,尤其是在编写旨在运行于较旧或限制更严格的系统上的脚本时。这一限制通常是早期内核实现的遗留问题,在处理复杂的脚本配置时,有时会成为令人困扰的问题。
Security Considerations and setuid Scripts
安全考量与 setuid 脚本
Security is a paramount concern in any operating system, and the shebang mechanism is no exception. When a script is executed with elevated privileges (setuid), the system must take extra precautions to prevent security vulnerabilities. One common issue is the potential for malicious users to exploit the shebang mechanism to execute arbitrary code with elevated privileges. Different systems employ various strategies to mitigate these risks, such as restricting the use of setuid scripts or carefully validating the interpreter path specified in the shebang line. Understanding these security implications is crucial for writing secure scripts and preventing potential exploits. The interaction between the shebang and setuid is a complex topic, and developers must be vigilant to avoid introducing vulnerabilities.
安全性是任何操作系统都至关重要的考量因素,shebang 机制也不例外。当脚本以提升的权限(setuid)执行时,系统必须采取额外的预防措施以防止安全漏洞。一个常见问题是,恶意用户可能利用 shebang 机制以提升的权限执行任意代码。不同系统采用多种策略来降低这些风险,例如限制 setuid 脚本的使用,或仔细验证 shebang 行中指定的解释器路径。理解这些安全影响对于编写安全的脚本和防范潜在的攻击至关重要。shebang 与 setuid 之间的相互作用是一个复杂的主题,开发人员必须保持警惕,以避免引入漏洞。
The Role of /usr/bin/env
/usr/bin/env 的作用
The /usr/bin/env utility plays a significant role in the shebang mechanism, particularly in enhancing script portability. By using #!/usr/bin/env interpreter, the script relies on the env utility to locate the interpreter in the system’s PATH environment variable. This approach avoids hardcoding the interpreter’s absolute path, making the script more portable across different systems where the interpreter might be located in different directories. However, the use of /usr/bin/env also introduces its own set of considerations and potential issues, such as reliance on the PATH environment being correctly configured. Despite these considerations, /usr/bin/env remains a powerful tool for improving script portability. It’s a common practice, and many seasoned scriptwriters swear by it.
/usr/bin/env 工具在 shebang 机制中扮演着重要角色,尤其是在提升脚本可移植性方面。通过使用 #!/usr/bin/env interpreter,脚本依赖 env 工具在系统的 PATH 环境变量中查找解释器。这种方式避免了硬编码解释器的绝对路径,使得脚本在不同系统上更具可移植性——因为在这些系统中,解释器可能位于不同的目录中。然而,使用 /usr/bin/env 也会带来一系列自身的考量和潜在问题,例如依赖于 PATH 环境变量的正确配置。尽管存在这些考量,/usr/bin/env 仍然是提升脚本可移植性的强大工具。这是一种常见做法,许多经验丰富的脚本编写者对其深信不疑。
Practical Implications and Best Practices
实际意义与最佳实践
Understanding the intricacies of the shebang mechanism is not just an academic exercise; it has practical implications for script development and system administration. By being aware of the nuances in argument passing, line length limitations, security considerations, and the role of /usr/bin/env, developers can write more robust, portable, and secure scripts. Adhering to best practices ensures that scripts behave predictably across different environments and reduces the risk of unexpected issues.
理解 shebang 机制的复杂细节不仅仅是一项学术研究——它对脚本开发和系统管理具有实际意义。通过了解参数传递、行长度限制、安全考量以及 /usr/bin/env 作用等方面的细节,开发人员能够编写更健壮、可移植且安全的脚本。遵循最佳实践能够确保脚本在不同环境中表现可预测,并降低出现意外问题的风险。
Ensuring Portability
确保可移植性
To maximize script portability, it’s crucial to use the shebang mechanism wisely. Employing /usr/bin/env is a common strategy, but it’s also important to be mindful of the potential pitfalls. Always ensure that the required interpreter is in the system’s PATH, and be aware of any system-specific quirks that might affect script execution. Testing scripts on different platforms is also essential to identify and address any portability issues. Portability is often an overlooked aspect of scripting, but it’s a critical factor for long-term maintainability and widespread usability.
为了最大限度地提升脚本的可移植性,明智地使用 shebang 机制至关重要。使用 /usr/bin/env 是一种常见策略,但同时也必须注意其潜在的陷阱。务必确保所需的解释器位于系统的 PATH 中,并了解可能影响脚本执行的任何系统特定特性。在不同平台上测试脚本也至关重要,以便识别并解决任何可移植性问题。可移植性通常是脚本编写中被忽视的方面,但它对于长期可维护性和广泛可用性而言是一个关键因素。
Writing Secure Scripts
编写安全的脚本
Security should always be a top priority when writing scripts, especially those that run with elevated privileges. Avoid using setuid scripts whenever possible, and if you must use them, exercise extreme caution. Validate all inputs, and be wary of potential exploits related to the shebang mechanism. Keep your systems and interpreters up-to-date with the latest security patches to mitigate known vulnerabilities. A secure script is a responsible script, protecting both the system and its users.
编写脚本时,安全性应始终是首要考量,尤其是对于那些以提升权限运行的脚本。尽可能避免使用 setuid 脚本,如果必须使用,则需格外谨慎。验证所有输入,并警惕与 shebang 机制相关的潜在攻击。保持系统和解释器更新至最新的安全补丁,以缓解已知漏洞。安全的脚本是负责任的脚本,能够保护系统及其用户。
Best Practices for Shebang Usage
Shebang 使用最佳实践
Some best practices for using the shebang mechanism include: always include a shebang line in your scripts, use /usr/bin/env for portability, be mindful of line length limitations, and understand the security implications of setuid scripts. By following these guidelines, you can write scripts that are reliable, portable, and secure. Consistent application of these practices will lead to cleaner, more maintainable codebases. A well-formed shebang line is the first step towards a well-behaved script.
使用 shebang 机制的一些最佳实践包括:脚本中始终包含 shebang 行、使用 /usr/bin/env 提升可移植性、注意行长度限制,以及理解 setuid 脚本的安全影响。遵循这些准则,您可以编写可靠、可移植且安全的脚本。持续应用这些实践将带来更简洁、更易于维护的代码库。格式规范的 shebang 行是编写行为良好的脚本的第一步。
Conclusion
结论
The Unix shebang mechanism, though seemingly simple, is a powerful and nuanced feature of Unix-like operating systems. Its history, implementation details, and practical implications are essential knowledge for anyone who writes or administers shell scripts. By understanding the intricacies of the shebang, developers can write more robust, portable, and secure scripts, ensuring that their code behaves predictably across different environments.
Unix shebang 机制虽然表面上简单,但却是类 Unix 操作系统中一项强大且复杂的功能。其历史、实现细节和实际意义,是所有编写或管理 shell 脚本的人员必备的知识。通过理解 shebang 的复杂细节,开发人员能够编写更健壮、可移植且安全的脚本,确保其代码在不同环境中表现可预测。
For further reading on this topic, consider exploring resources from trusted sources like The Linux Documentation Project, which offers a wealth of information on various aspects of Linux and Unix systems.
如需深入了解该主题,可参考可靠来源的资源,例如 Linux 文档项目,该项目提供了有关 Linux 和 Unix 系统各个方面的丰富信息。
Bash Shebang: The Ultimate Guide to Script Interpreters
Bash Shebang:脚本解释器终极指南
Dec 8, 2025
If you’ve ever written or run a shell script, you’ve likely encountered the mysterious line at the top: #!/bin/bash. This unassuming line, known as the shebang (pronounced “shuh-bang”), is far more than a comment—it’s a critical directive that tells your operating system which interpreter to use to execute the script. Whether you’re a beginner learning shell scripting or an experienced developer debugging cross-platform issues, understanding the bash shebang is essential for writing reliable, portable scripts.
如果你曾经编写或运行过 shell 脚本,大概率会遇到文件顶部那行神秘的代码:#!/bin/bash。这行看似不起眼的代码被称为 shebang(发音为 “shuh-bang”,意为“哈希邦”),它绝非普通注释——而是一条关键指令,用于告知操作系统应使用哪个解释器来执行该脚本。无论你是初学 shell 脚本的新手,还是正在调试跨平台问题的资深开发人员,理解 bash shebang 都是编写可靠、可移植脚本的核心前提。
In this guide, we’ll demystify the shebang: what it is, how it works, common pitfalls, best practices, and practical examples to ensure your scripts run smoothly across systems.
本指南将为你揭开 shebang 的神秘面纱:包括其定义、工作原理、常见陷阱、最佳实践以及实用示例,帮助你确保脚本在不同系统中顺畅运行。
What is the Bash Shebang?
什么是 Bash Shebang?
The shebang (also called the “hashbang”) is a special line at the very start of a script that specifies the path to the interpreter responsible for executing the script. Its name derives from the characters # (hash or sharp) and ! (bang), which are the first two characters of the line.
shebang(也称为“hashbang”)是脚本最开头的一行特殊代码,用于指定负责执行该脚本的解释器路径。其名称来源于该行的前两个字符:#(hash 或 sharp,意为“井号”)和 !(bang,意为“感叹号”)。
For bash scripts, the shebang typically looks like:
对于 bash 脚本,shebang 通常如下所示:
#!/bin/bash
This line tells the operating system: “Use the bash interpreter located at /bin/bash to run this script.” Without a shebang, the system may default to a different shell (e.g., sh, the POSIX-compliant shell), which can cause unexpected behavior if your script uses bash-specific features (e.g., arrays, [[ ]] conditionals).
这行代码告知操作系统:“使用位于 /bin/bash 路径下的 bash 解释器来运行此脚本。” 若缺少 shebang,系统可能会默认使用其他 shell(例如符合 POSIX 标准的 sh),如果你的脚本使用了 bash 专属特性(如数组、[[ ]] 条件判断),就可能出现意外行为。
Syntax and Structure
语法与结构
The shebang has a strict syntax:
shebang 具有严格的语法格式:
#!/path/to/interpreter [optional-arguments]
#!/解释器绝对路径 [可选参数]
Key Rules:
核心规则:
- Must be the first line: No characters (including whitespace) can precede
#!. Even a blank line or comment before the shebang will cause it to be ignored.
必须位于第一行:#!之前不得有任何字符(包括空格)。即使 shebang 前有空白行或注释,都会导致其被忽略。 - Hash-bang sequence: The line must start with
#!(no exceptions).
固定字符组合:该行必须以#!开头(无例外)。 - Interpreter path: The path to the interpreter (e.g.,
/bin/bash) must be an absolute path (or useenvfor portability, discussed later).
解释器路径:解释器的路径(如/bin/bash)必须是绝对路径(或使用env提升可移植性,后续将详细说明)。 - Optional arguments: You can pass arguments to the interpreter (e.g.,
#!/bin/bash -eto exit on error), but avoid complex arguments (some systems have limitations here).
可选参数:可向解释器传递参数(例如#!/bin/bash -e表示遇到错误时退出),但应避免复杂参数(部分系统对此存在限制)。
How the Shebang Works
Shebang 的工作原理
When you execute a script (e.g., ./my_script.sh), the operating system (kernel) reads the first two bytes of the file. If they are #! (the “magic number” for scripts), the kernel parses the rest of the line to find the interpreter path. It then launches the interpreter and passes the script’s path as an argument to it.
当你执行一个脚本(例如 ./my_script.sh)时,操作系统(内核)会读取文件的前两个字节。如果这两个字节是 #!(脚本的“魔数”),内核会解析该行剩余部分以找到解释器路径,随后启动该解释器,并将脚本路径作为参数传入。
For example, running ./my_script.sh with #!/bin/bash is equivalent to running:
例如,运行带有 #!/bin/bash 的 ./my_script.sh,等同于执行命令:
/bin/bash ./my_script.sh
If no shebang exists, the kernel defaults to the current shell (e.g., $SHELL), which may not be bash. This is why scripts with bash-specific code often fail when run without a shebang.
若不存在 shebang,内核会默认使用当前 shell(例如 $SHELL 环境变量指定的 shell),而该 shell 可能并非 bash。这也是为什么包含 bash 专属代码的脚本在缺少 shebang 时往往会执行失败。
Common Shebang Directives
常见 Shebang 指令
While #!/bin/bash is the most common, other shebangs exist for different interpreters. Here are the most useful ones for bash scripting:
虽然 #!/bin/bash 最为常用,但针对不同解释器还有其他 shebang 格式。以下是 bash 脚本中最实用的几种:
1. #!/bin/bash
The standard shebang for bash scripts. Uses the bash interpreter at the fixed path /bin/bash.
bash 脚本的标准 shebang,使用固定路径 /bin/bash 下的 bash 解释器。
Pros: Explicit and reliable on systems where bash lives in /bin.
优点:在 bash 位于 /bin 目录的系统上,指令明确且可靠。
Cons: Less portable—some systems (e.g., BSD, macOS) may have bash in /usr/local/bin/bash instead.
缺点:可移植性较差——部分系统(如 BSD、macOS)的 bash 可能位于 /usr/local/bin/bash 路径下。
2. #!/usr/bin/env bash
Uses the env utility to locate bash in the system’s PATH.
通过 env 工具在系统的 PATH 环境变量中查找 bash 解释器。
Pros: Highly portable. env searches PATH for bash, so it works even if bash is in /usr/bin/bash, /usr/local/bin/bash, or another location.
优点:可移植性极强。env 会在 PATH 中搜索 bash,因此无论 bash 位于 /usr/bin/bash、/usr/local/bin/bash 还是其他路径,都能正常工作。
Cons: Slightly slower (negligible) and requires env to be in /usr/bin (almost universal on Unix-like systems).
缺点:执行速度略有下降(影响可忽略),且要求 env 工具位于 /usr/bin 路径下(类 Unix 系统几乎都满足此条件)。
Why this works: env is a standard utility that runs programs in a modified environment. env bash finds bash by searching the PATH variable, making it robust to path variations.
工作原理:env 是一款标准工具,用于在修改后的环境中运行程序。env bash 通过搜索 PATH 环境变量找到 bash,因此能适应不同系统中解释器路径的差异。
3. #!/bin/sh
For POSIX-compliant scripts (no bash-specific features). Uses the system’s default sh (often a symlink to bash, dash, or ksh).
适用于符合 POSIX 标准的脚本(无 bash 专属特性),使用系统默认的 sh 解释器(通常是 bash、dash 或 ksh 的符号链接)。
4. Other Interpreters
其他解释器
Shebangs work for non-shell scripts too:
shebang 同样适用于非 shell 脚本:
- Python:
#!/usr/bin/env python3 - Perl:
#!/usr/bin/perl - Ruby:
#!/usr/bin/env ruby
Practical Examples
实用示例
Let’s walk through examples to see the shebang in action.
下面通过示例演示 shebang 的实际应用。
Example 1: Basic Bash Script with Shebang
示例 1:带 Shebang 的基础 Bash 脚本
Create hello_bash.sh:
创建 hello_bash.sh 文件:
#!/bin/bash
echo "Hello from bash!"
# Check which shell is running
echo "Current shell: $0"
Make it executable and run:
赋予执行权限并运行:
chmod +x hello_bash.sh
./hello_bash.sh
Output:
输出:
Hello from bash!
Current shell: /bin/bash
Example 2: Using env for Portability
示例 2:使用 env 提升可移植性
On some systems (e.g., macOS), bash may be in /usr/local/bin/bash instead of /bin/bash. Use env to avoid this issue:
在部分系统(如 macOS)中,bash 可能位于 /usr/local/bin/bash 而非 /bin/bash。使用 env 可避免此问题:
Create hello_portable.sh:
创建 hello_portable.sh 文件:
#!/usr/bin/env bash
echo "Hello from portable bash!"
echo "Bash path: $(which bash)" # Show where bash is located
Run it:
运行脚本:
chmod +x hello_portable.sh
./hello_portable.sh
Output (on macOS):
输出(macOS 系统):
Hello from portable bash!
Bash path: /usr/local/bin/bash
Example 3: What Happens Without a Shebang?
示例 3:缺少 Shebang 会发生什么?
Create no_shebang.sh (no shebang line):
创建 no_shebang.sh 文件(无 shebang 行):
echo "Hello from default shell!"
echo "Current shell: $0"
Run it:
运行脚本:
chmod +x no_shebang.sh
./no_shebang.sh
Output (may vary):
输出(可能因系统而异):
Hello from default shell!
Current shell: /bin/sh
Here, the system defaulted to sh, which lacks bash features. If your script uses [[ ]] (bash-specific), it would fail:
此时系统默认使用 sh 解释器,而 sh 不支持 bash 专属特性。若脚本中使用 [[ ]](bash 专属语法),会执行失败:
# no_shebang.sh with bashism
[[ "foo" == "foo" ]] && echo "Match!" # Works in bash, fails in sh
Running this would throw an error:
运行上述脚本会抛出错误:
./no_shebang.sh: 2: [[: not found
./no_shebang.sh: 2: 未找到 [[ 命令
Best Practices
最佳实践
To avoid issues with shebangs, follow these guidelines:
为避免 shebang 相关问题,请遵循以下准则:
1. Use #!/usr/bin/env bash for Portability
1. 使用 #!/usr/bin/env bash 提升可移植性
Most systems have env in /usr/bin, and env bash finds bash in PATH. This avoids hardcoding paths like /bin/bash (which may differ across systems).
大多数系统的 env 位于 /usr/bin 路径下,env bash 会在 PATH 中查找 bash,避免了硬编码 /bin/bash 这类可能因系统而异的路径。
2. Avoid Leading Whitespace
2. 避免开头空格
Even a single space before #! breaks the shebang:
#! 前即使只有一个空格,也会导致 shebang 失效:
#!/bin/bash # ❌ Leading space: shebang ignored!
echo "This will run in sh, not bash!"
#!/bin/bash # ❌ 开头有空格:shebang 被忽略!
echo "此脚本将在 sh 中运行,而非 bash!"
3. Test Interpreter Paths
3. 验证解释器路径
Verify the interpreter exists with which:
使用 which 命令验证解释器是否存在:
which bash # Output: /bin/bash (or another path)
which bash # 输出:/bin/bash(或其他路径)
4. Keep the Shebang Line Clean
4. 保持 Shebang 行简洁
Don’t add comments or extra text to the shebang line:
不要在 shebang 行添加注释或多余文本:
#!/bin/bash This is a comment # ❌ Invalid! Shebang line cannot have comments.
#!/bin/bash 这是注释 # ❌ 无效!Shebang 行不能包含注释。
Add comments after the shebang instead:
应在 shebang 行之后添加注释:
#!/bin/bash
# This is a valid comment (on the next line) 这是有效的注释(位于下一行)
echo "Hello!"
5. Handle Line Endings
5. 处理行结束符
Windows uses CRLF (\r\n) line endings, while Unix uses LF (\n). A shebang with CRLF endings may appear as #!/bin/bash\r to the kernel, which will fail (the \r is treated as part of the path). Use tools like dos2unix to convert line endings:
Windows 系统使用 CRLF(\r\n)作为行结束符,而 Unix 系统使用 LF(\n)。带有 CRLF 结束符的 shebang 会被内核识别为 #!/bin/bash\r(\r 被视为路径的一部分),导致执行失败。可使用 dos2unix 等工具转换行结束符:
dos2unix my_script.sh # Converts CRLF to LF
dos2unix my_script.sh # 将 CRLF 转换为 LF
6. Make Scripts Executable
6. 赋予脚本执行权限
Always set execute permissions with chmod +x my_script.sh to run scripts directly.
务必通过 chmod +x my_script.sh 为脚本设置执行权限,以便直接运行。
Troubleshooting Common Issues
常见问题排查
If your shebang isn’t working, check these issues:
若 shebang 无法正常工作,请排查以下问题:
1. “Permission Denied” Error
1. “权限被拒绝”错误
Cause: The script isn’t executable.
脚本未设置执行权限。
Fix: Run chmod +x my_script.sh.
执行 chmod +x my_script.sh。
2. “No such file or directory”
2. “没有那个文件或目录”
Cause: The interpreter path is invalid (e.g., #!/wrong/path/bash).
解释器路径无效(例如 #!/wrong/path/bash)。
Fix: Use which bash to find the correct path, or switch to #!/usr/bin/env bash.
使用 which bash 查找正确路径,或切换为 #!/usr/bin/env bash。
3. Script Runs in Wrong Shell
3. 脚本在错误的 Shell 中运行
Cause: Shebang is missing, or there’s leading whitespace.
缺少 shebang,或 shebang 前有开头空格。
Fix: Ensure the shebang is the first line with no leading characters.
确保 shebang 位于第一行,且前面无任何字符。
4. “Bad Interpreter: Text file busy”
4. “错误的解释器:文本文件正忙”
Cause: The script is open in an editor or being written to.
脚本正在编辑器中打开,或正在被写入内容。
Fix: Close the file and try again.
关闭该文件后重试。
5. CRLF Line Endings
5. CRLF 行结束符问题
Symptom: Shebang fails with a cryptic error like #!/bin/bash^M: bad interpreter.
现象:shebang 执行失败,抛出类似 #!/bin/bash^M: 错误的解释器 的模糊错误。
Fix: Convert line endings to LF with dos2unix my_script.sh or use a text editor (e.g., VS Code) to set line endings to “Unix (LF)”.
使用 dos2unix my_script.sh 将行结束符转换为 LF,或通过文本编辑器(如 VS Code)将行结束符设置为“Unix (LF)”。
6. Interpreter Not in PATH (for env Shebang)
6. 解释器不在 PATH 中(适用于 env 类 Shebang)
Symptom: Error like /usr/bin/env: 'bash': No such file or directory.
现象:抛出类似 /usr/bin/env: 'bash': 没有那个文件或目录 的错误。
Cause: bash is not installed, or its path is not included in the system’s PATH variable.
未安装 bash,或 bash 的路径未包含在系统的 PATH 环境变量中。
Fix: Install bash (e.g., sudo apt install bash on Debian/Ubuntu) or add its path to PATH (e.g., export PATH="/usr/local/bin:$PATH").
安装 bash(如 Debian/Ubuntu 系统执行 sudo apt install bash),或将其路径添加到 PATH 中(如 export PATH="/usr/local/bin:$PATH")。
Advanced Tips
进阶技巧
1. Passing Arguments to the Interpreter
1. 向解释器传递参数
You can append arguments to the shebang line to modify the interpreter’s behavior. Common useful arguments for bash:
可在 shebang 行后追加参数以修改解释器行为。bash 常用的实用参数:
-e: Exit immediately if any command fails (avoids silent errors).
-e:任何命令执行失败时立即退出(避免静默错误)。-u: Treat unset variables as errors (catches typos in variable names).
-u:将未定义的变量视为错误(捕获变量名拼写错误)。-x: Print each command before execution (useful for debugging).
-x:执行前打印每个命令(调试时实用)。-o pipefail: Make pipelines fail if any command in the pipeline fails (not just the last one).
-o pipefail:管道中任一命令失败时,整个管道视为失败(而非仅最后一个命令)。
Example:
示例:
#!/usr/bin/env bash -euo pipefail
# 脚本将在命令失败、使用未定义变量或管道出错时退出
echo "Running with strict mode enabled!"
2. Using env -S for Multiple Arguments (GNU Coreutils)
2. 使用 env -S 传递多个参数(GNU Coreutils)
Most systems treat all text after the interpreter path in the shebang as a single argument. For example, #!/usr/bin/env perl -T -w fails because perl -T -w is interpreted as one filename.
大多数系统会将 shebang 中解释器路径后的所有文本视为单个参数。例如 #!/usr/bin/env perl -T -w 会执行失败,因为 perl -T -w 被当作单个文件名处理。
The env -S option (supported in GNU Coreutils 8.30+) splits the string into multiple arguments, solving this issue:
env -S 选项(GNU Coreutils 8.30+ 支持)可将字符串拆分为多个参数,解决此问题:
#!/usr/bin/env -S perl -T -w
# 等效于执行 perl -T -w 脚本名
print "Running with multiple interpreter arguments!\n";
3. Restricted Shell Shebang
3. 受限 Shell Shebang
For secure scripts that limit user actions, use the restricted bash shell with #!/bin/bash -r. This disables dangerous operations like changing directories (cd) or modifying PATH.
对于需要限制用户操作的安全脚本,可使用受限 bash shell:#!/bin/bash -r。这会禁用切换目录(cd)、修改 PATH 等危险操作。
4. Shebang for Bash Modules or Libraries
4. Bash 模块/库的 Shebang
If you’re writing a bash library (sourced with source ./lib.sh instead of executed directly), you can still include a shebang for clarity—though it won’t be used when sourcing:
若编写 bash 库(通过 source ./lib.sh 引用而非直接执行),仍可添加 shebang 以提升可读性——尽管引用时不会生效:
#!/usr/bin/env bash
# 此脚本为 bash 库,需通过 source 引用
my_function() {
echo "Hello from the library!"
}
Conclusion
结论
The bash shebang is a small yet powerful and critical component of shell scripting. It dictates how scripts are executed, ensuring they run with the correct interpreter and avoiding bugs caused by shell differences or improper configuration.
bash shebang 是 shell 脚本中小巧却强大且关键的核心组件。它决定了脚本的执行方式,确保脚本以正确的解释器运行,同时避免因 shell 差异或配置不当引发的漏洞。
By mastering its syntax, understanding its interaction with the kernel and system environment, and adhering to best practices—such as using #!/usr/bin/env bash for portability, keeping the shebang as the first line with no leading whitespace, and verifying interpreter paths—you can eliminate cross-platform issues and write scripts that are reliable, secure, and easy to maintain.
掌握其语法规则、理解它与内核及系统环境的交互逻辑,并遵循以下最佳实践——为提升可移植性使用 #!/usr/bin/env bash、将 shebang 置于文件首行且无前置空格、验证解释器路径有效性——你就能解决跨平台兼容问题,编写可靠、安全且易于维护的脚本。
Whether you’re automating simple tasks or building complex shell applications, the shebang is the foundation of consistent and predictable script execution. Remember: A well-crafted shebang saves hours of debugging. Invest the time to get it right—your future self and other developers will thank you. Happy scripting!
无论你是自动化简单任务,还是构建复杂的 shell 应用,shebang 都是脚本实现一致、可预测执行的基础。谨记:一条精心编写的 shebang 可节省数小时调试时间。花时间把它做好——你未来的自己和其他开发人员都会为此感激。脚本编写愉快!
# ! | shebang
by detailedpedia
In computing, a shebang is the character sequence #!, consisting of the characters number sign (also known as sharp or hash) and exclamation mark (also known as bang), at the beginning of a script. It is also called sharp-exclamation, sha-bang,[1][2]hashbang,[3][4]pound-bang,[5][6]or hash-pling.[7]
在计算中,shebang 是字符序列 #!,由井号(亦称 sharp 或 hash)和感叹号(亦称 bang)组成,位于脚本开头。它也被称为 sharp-exclamation、sha-bang、[1][2]hashbang、[3][4]pound-bang、[5][6]或 hash-pling。[7]
When a text file with a shebang is used as if it were an executable in a Unix-like operating system, the program loader mechanism parses the rest of the file’s initial line as an interpreter directive. The loader executes the specified interpreter program, passing to it as an argument the path that was initially used when attempting to run the script, so that the program may use the file as input data.[8] For example, if a script is named with the path path/to/script, and it starts with the line #! /bin/sh, then the program loader is instructed to run the program /bin/sh, passing path/to/script as the first argument.
当带有 shebang 的文本文件在类 Unix操作系统中被当作可执行文件使用时,程序加载器机制会把文件首行的剩余部分解析为解释器指令。加载器执行指定的解释器程序,并将最初尝试运行该脚本时所用的路径作为参数传给它,以便该程序可将文件用作输入数据。[8]例如,若脚本路径为 path/to/script,且以 #! /bin/sh 开头,则程序加载器被指示运行 /bin/sh 程序,并以 path/to/script 作为第一个参数。
The shebang line is usually ignored by the interpreter, because the “#” character is a comment marker in many scripting languages; some language interpreters that do not use the hash mark to begin comments still may ignore the shebang line in recognition of its purpose.[9]
shebang 行通常被解释器忽略,因为“#”字符在许多脚本语言中是注释标记;某些不以井号起注释的语言解释器也可能因识别其目的而忽略 shebang 行。[9]
Syntax
语法
The form of a shebang interpreter directive is as follows:[8]
shebang 解释器指令的形式如下:[8]
#! interpreter [optional-one-arg-only]
in which interpreter is a path to an executable program. The space between #! and interpreter is optional. There could be any number of spaces or tabs either before or after interpreter. The optional-arg will include any extra spaces up to the end-of-line.
其中 interpreter 是可执行程序的路径。#! 与 interpreter 之间的空格可选;前后可有任意数量的空格或制表符。optional-arg 将包含到行尾的所有额外空格。
In Linux, the file specified by interpreter can be executed if it has the execute rights and is one of the following:
在Linux中,若 interpreter 指定的文件具有执行权限且符合以下之一,即可被执行:
-
a native executable, such as an ELF binary
原生可执行文件,如 ELF 二进制 -
any kind of file for which an interpreter was registered via the binfmt_misc mechanism (such as for executing Microsoft .exe binaries using wine)
通过 binfmt_misc 机制注册了解释器的任意文件(如用 wine 执行微软 .exe 二进制) -
another script starting with a shebang
以 shebang 开头的另一脚本
On Linux and Minix, an interpreter can also be a script. A chain of shebangs and wrappers yields a directly executable file that gets the encountered scripts as parameters in reverse order. For example, if file /bin/A is an executable file in ELF format, file /bin/B contains the shebang #! /bin/A optparam, and file /bin/C contains the shebang #! /bin/B, then executing file /bin/C resolves to /bin/B /bin/C, which finally resolves to /bin/A optparam /bin/B /bin/C.
在 Linux 与 Minix 上,解释器也可以是脚本。shebang 与包装器可形成链,生成直接可执行文件,并以逆序将遇到的脚本作为参数。例如,若文件 /bin/A 为 ELF 可执行,文件 /bin/B 含 shebang #! /bin/A optparam,文件 /bin/C 含 shebang #! /bin/B,则执行 /bin/C 解析为 /bin/B /bin/C,最终解析为 /bin/A optparam /bin/B /bin/C。
In Solaris- and Darwin-derived operating systems (such as macOS), the file specified by interpreter must be an executable binary and cannot itself be a script.[10]
在 Solaris 与 Darwin 衍生系统(如 macOS)中,interpreter 指定的文件必须是可执行二进制,不能是脚本。[10]
Examples
示例
Some typical shebang lines:
典型 shebang 行:
-
#! /bin/sh– Execute the file using the Bourne shell, or a compatible shell, assumed to be in the /bin directory
#! /bin/sh– 使用假定位于 /bin 的 Bourne shell 或兼容 shell 执行文件 -
#! /bin/bash– Execute the file using the Bash shell
#! /bin/bash– 使用 Bash shell 执行文件 -
#! /usr/bin/pwsh– Execute the file using PowerShell
#! /usr/bin/pwsh– 使用 PowerShell 执行文件 -
#! /usr/bin/env python3– Execute with a Python interpreter, using the env program search path to find it
#! /usr/bin/env python3– 使用 Python 解释器执行,并通过 env 程序搜索路径定位 -
#! /bin/false– Do nothing, but return a non-zero exit status, indicating failure. Used to prevent stand-alone execution of a script file intended for execution in a specific context, such as by the**.**command from sh/bash,sourcefrom csh/tcsh, or as a .profile, .cshrc, or .login file.
#! /bin/false– 什么都不做,但返回非零退出状态,表示失败。 用于防止脚本被独立执行,而仅限特定上下文(如 sh/bash 的**.**命令、csh/tcsh 的source,或 .profile、.cshrc、.login 文件)。
Shebang lines may include specific options that are passed to the interpreter. However, implementations vary in the parsing behavior of options; for portability, only one option should be specified without any embedded whitespace.[11] Further portability guidelines are found below.
shebang 行可包含传递给解释器的特定选项。然而,各实现解析选项的行为不同;为可移植性,应仅指定一个选项且不含嵌入空白。[11] 更多可移植性指南见下文。
undefined
Purpose
目的
Interpreter directives allow scripts and data files to be used as commands, hiding the details of their implementation from users and other programs, by removing the need to prefix scripts with their interpreter on the command line.
解释器指令使脚本与数据文件可被当作命令使用,无需在命令行前缀解释器,从而对用户和其他程序隐藏实现细节。
For example, consider a script having the initial line #! /bin/sh -x. It may be invoked simply by giving its file path, such as some/path/to/foo,[12] and some parameters, such as bar and baz:
例如,某脚本首行为 #! /bin/sh -x,只需给出文件路径(如 some/path/to/foo)[12] 及参数 bar、baz 即可调用:
some/path/to/foo bar baz
In this case /bin/sh is invoked in its place, with parameters -x, some/path/to/foo, bar, and baz, as if the original command had been
此时 /bin/sh 被调用,参数为 -x、some/path/to/foo、bar、baz,等同于原命令:
/bin/sh -x some/path/to/foo bar baz
Most interpreters make any additional arguments available to the script. If /bin/sh is a POSIX-compatible shell, then bar and baz are presented to the script as the positional parameter array "$@", and individually as parameters "$1" and "$2" respectively.
多数解释器会将额外参数提供给脚本。若 /bin/sh 为 POSIX 兼容 shell,则 bar 与 baz 分别作为位置参数数组 "$@" 及单个参数 "$1"、"$2" 传入。
Because the initial # is the character used to introduce comments in the POSIX shell language (and in the languages understood by many other interpreters), the whole shebang line is ignored by the interpreter. However, it is up to the interpreter to ignore the shebang line, and not all do so; thus, a script consisting of the following two lines simply outputs both lines when run:
由于首字符 # 在 POSIX shell语言(及许多其他解释器语言)中用于引入注释,整个 shebang 行会被解释器忽略。然而,是否忽略取决于解释器,并非全部如此;因此,以下两行脚本运行时仅输出两行:
#! /bin/cat
Hello world!
Strengths
优势
When compared to the use of global association lists between file extensions and the interpreting applications, the interpreter directive method allows users to use interpreters not known at a global system level, and without administrator rights. It also allows specific selection of interpreter, without overloading the filename extension namespace (where one file extension refers to more than one file type), and allows the implementation language of a script to be changed without changing its invocation syntax by other programs. Invokers of the script need not know what the implementation language is as the script itself is responsible for specifying the interpreter to use.
与全局文件扩展名—解释器应用关联列表相比,解释器指令方法允许用户使用系统全局未知、无需管理员权限的解释器;可在不挤占文件名扩展命名空间(同一扩展名对应多种文件类型)的情况下精确选择解释器;并可在不改变其他程序调用语法的前提下更换脚本实现语言。调用者无需知晓实现语言,因为脚本自身负责指定所用解释器。
Portability
可移植性
Program location
程序位置
Shebangs must specify absolute paths (or paths relative to current working directory) to system executables; this can cause problems on systems that have a non-standard file system layout. Even when systems have fairly standard paths, it is quite possible for variants of the same operating system to have different locations for the desired interpreter. Python, for example, might be in /usr/bin/python3, /usr/local/bin/python3, or even something like /home/username/bin/python3 if installed by an ordinary user.
shebang 必须指定系统可执行文件的绝对路径(或相对于当前工作目录的路径);这在非标准文件系统布局的系统上可能引发问题。即便路径相当标准,同一操作系统的不同变种也可能将目标解释器放在不同位置。例如,Python 可能在 /usr/bin/python3、/usr/local/bin/python3,或由普通用户安装的 /home/username/bin/python3。
A similar problem exists for the POSIX shell, since POSIX only required its name to be sh, but did not mandate a path. A common value is /bin/sh, but some systems such as Solaris have the POSIX-compatible shell at /usr/xpg4/bin/sh.[13] In many Linux systems, /bin/sh is a ard or symbolic link to /bin/bash, the Bourne Again shell (BASH). Using bash-specific syntax while maintaining a shebang pointing to sh is also not portable.[14]
POSIX shell 也存在类似问题,因为 POSIX 仅要求其名为 sh,而未规定路径。常见值为 /bin/sh,但某些系统(如 Solaris)将 POSIX 兼容 shell 放在 /usr/xpg4/bin/sh。[13] 在许多 Linux 系统上,/bin/sh 是到 Bourne Again shell(BASH)的硬链接或符号链接。在 shebang 指向 sh 的同时使用 bash 专有语法也不具可移植性。[14]
Because of this it is sometimes required to edit the shebang line after copying a script from one computer to another because the path that was coded into the script may not apply on a new machine, depending on the consistency in past convention of placement of the interpreter. For this reason and because POSIX does not standardize path names, POSIX does not standardize the feature.[15] The GNU Autoconf tool can test for system support with the macro AC_SYS_INTERPRETER.[16]
因此,在将脚本从一台计算机复制到另一台后,有时需要编辑 shebang 行,因为脚本中硬编码的路径可能在新机器上并不适用,这取决于过去解释器放置位置的惯例一致性。为此,也由于 POSIX 未标准化路径名,POSIX 并未标准化此特性。[15] GNU Autoconf 工具可通过宏 AC_SYS_INTERPRETER 测试系统支持。[16]
Often, the program /usr/bin/env can be used to circumvent this limitation by introducing a level of indirection. #! is followed by /usr/bin/env, followed by the desired command without full path, as in this example:
通常,程序 /usr/bin/env 可通过引入一层间接来规避此限制。#! 后跟 /usr/bin/env,再跟无需完整路径的目标命令,例如:
#!/usr/bin/env sh
This mostly works because the path /usr/bin/env is commonly used for the env utility, and it invokes the first sh found in the user’s $PATH, typically /bin/sh.
这通常有效,因为 /usr/bin/env 常用作 env 工具的路径,它会调用用户 $PATH 中第一个找到的 sh,通常是 /bin/sh。
This particular example (using sh) is of limited utility: neither /bin/sh nor /usr/bin/env is universal, with similar numbers of devices lacking each. More broadly using #!/usr/bin/env for any script still has some portability issues with OpenServer 5.0.6 and Unicos 9.0.2 which have only /bin/env and no /usr/bin/env.
此特定示例(使用 sh)效用有限:/bin/sh 与 /usr/bin/env 均非普适,缺少两者的设备数量相近。更广泛地对任何脚本使用 #!/usr/bin/env 仍在 OpenServer 5.0.6 与 Unicos 9.0.2 上存在可移植问题,这些系统仅有 /bin/env 而无 /usr/bin/env。
Using #!/usr/bin/env results in run-time indirection, which has the potential to degrade system security; for this reason some commentators recommend against its use[17] in packaged software, reserving it only for “educational examples”.
使用 #!/usr/bin/env 带来运行期间接,可能降低系统安全性;因此有评论者建议[17]在打包软件中避免使用,仅留给“教学示例”。
Argument splitting
参数拆分
Command arguments are split in different ways across platforms. Some systems do not split up the arguments; for example, when running the script with the first line,
各平台对命令参数的拆分方式不同。某些系统不拆分参数;例如,当脚本首行为:
#!/usr/bin/env python3 -c
all text after the first space is treated as a single argument, that is, python3 -c will be passed as one argument to /usr/bin/env, rather than two arguments. Such systems include Linux[18][19] and Cygwin.
首个空格后的全部文本被当作单一参数,即 python3 -c 作为一个参数传给 /usr/bin/env,而非两个参数。此类系统包括 Linux[18][19] 与 Cygwin。
Another approach is the use of a wrapper. FreeBSD 6.0 (2005) introduced a -S option to its env as it changed the shebang-reading behavior to non-splitting. This option tells env to split the string itself.[20] The GNU env utility since coreutils 8.30 (2018) also includes this feature.[21] Although using this option mitigates the portability issue on the kernel end with splitting, it adds the requirement that env supports this particular extension.
另一种方法是使用包装器。FreeBSD 6.0(2005)在将 shebang 读取行为改为不拆分的同时,为其 env 引入了 -S 选项,该选项告诉 env 自行拆分字符串。[20] 自 coreutils 8.30(2018)起,GNU env 工具也包含此功能。[21] 尽管使用该选项可缓解内核端拆分带来的可移植问题,但它增加了 env 必须支持此特定扩展的要求。
Character interpretation
字符解释
Another problem is scripts containing a carriage return character immediately after the shebang line, perhaps as a result of being edited on a system that uses DOS line breaks, such as Microsoft Windows. Some systems interpret the carriage return character as part of the interpreter command, resulting in an error message.[22]
另一个问题是脚本在 shebang 行后紧接出现回车字符,可能是由于在采用 DOS 换行的系统(如 Microsoft Windows)上编辑所致。某些系统会将回车符解释为解释器命令的一部分,导致错误消息。[22]
Magic number
魔数
The shebang is actually a human-readable instance of a magic number in the executable file, the magic byte string being 0x23 0x21, the two-character encoding in ASCII of #!. This magic number is detected by the “exec” family of functions, which determine whether a file is a script or an executable binary. The presence of the shebang will result in the execution of the specified executable, usually an interpreter for the script’s language. It has been claimed[23] that some old versions of Unix expect the normal shebang to be followed by a space and a slash (#! /), but this appears to be untrue;[11] rather, blanks after the shebang have traditionally been allowed, and sometimes documented with a space, as described in the 1980 historical email below.
shebang 实际上是可执行文件中人类可读的魔数实例,其魔数字节串为 0x23 0x21,即 #! 的 ASCII 双字符编码。该魔数由“exec”函数族检测,用以判断文件是脚本还是可执行二进制。出现 shebang 将导致执行指定的可执行文件,通常是脚本语言的解释器。有人声称[23]某些旧版 Unix 期望普通 shebang 后跟一个空格和斜杠(#! /),但这似乎不实;[11] 相反,shebang 后的空白传统上一直被允许,有时在文档中也会带空格,如下方1980 年历史邮件所述。
The shebang characters are represented by the same two bytes in extended ASCII encodings, including UTF-8, which is commonly used for scripts and other text files on current Unix-like systems. However, UTF-8 files may begin with the optional byte order mark (BOM); if the “exec” function specifically detects the bytes 0x23 and 0x21, then the presence of the BOM (0xEF 0xBB 0xBF) before the shebang will prevent the script interpreter from being executed. Some authorities recommend against using the byte order mark in POSIX (Unix-like) scripts,[24] for this reason and for wider interoperability and philosophical concerns. Additionally, a byte order mark is not necessary in UTF-8, as that encoding does not have endianness issues; it serves only to identify the encoding as UTF-8.[24]
shebang 字符在扩展 ASCII 编码(包括当前类 Unix 系统常用于脚本及其他文本文件的 UTF-8)中由相同两个字节表示。然而,UTF-8 文件可能以可选的字节序标记(BOM)开头;若“exec”函数专门检测 0x23 与 0x21 字节,则 shebang 前的 BOM(0xEF 0xBB 0xBF)将阻止脚本解释器被执行。某些权威机构建议POSIX(类 Unix)脚本不要使用字节序标记,[24] 原因包括此隐患及更广泛的互操作性与哲学考量。此外,UTF-8 无需字节序标记,因为该编码无字节序问题;它仅用于标识编码为 UTF-8。[24]
Etymology
词源
An executable file starting with an interpreter directive is simply called a script, often prefaced with the name or general classification of the intended interpreter. The name shebang for the distinctive two characters may have come from an inexact contraction of SHArp bang or haSH bang, referring to the two typical Unix names for them. Another theory on the sh in shebang is that it is from the default shell sh, usually invoked with shebang.[25] This usage was current by December 1989,[26] and probably earlier.
以解释器指令开头的可执行文件通常就叫脚本,常冠以目标解释器的名称或通用分类。独有双字符名称 shebang 可能源自对 SHArp bang 或 haSH bang 的不精确缩合,指它们在 Unix 中的两个典型名称。另一种理论认为 shebang 中的 sh 来自默认 shell sh,该 shell 通常通过 shebang 调用。[25] 这一用法在 1989 年 12 月[26] 已出现,可能更早。
History
历史
The shebang was introduced by Dennis Ritchie between Edition 7 and 8 at Bell Laboratories. It was also added to the BSD releases from Berkeley’s Computer Science Research (present at 2.8BSD[27] and activated by default by 4.2BSD). As AT&T Bell Laboratories Edition 8 Unix, and later editions, were not released to the public, the first widely known appearance of this feature was on BSD.
shebang 由 Dennis Ritchie 在 Bell 实验室的 第 7 版 与 第 8 版 之间引入。它也被加入伯克利计算机科学研究的 BSD 发行版(2.8BSD[27] 已存在,4.2BSD 默认启用)。由于 AT&T Bell 实验室的第 8 版及后续版本未向公众发布,该特性首次广为人知是在 BSD 上。
The lack of an interpreter directive, but support for shell scripts, is apparent in the documentation from Version 7 Unix in 1979,[28] which describes instead a facility of the Bourne shell where files with execute permission would be handled specially by the shell, which would (sometimes depending on initial characters in the script, such as “:” or “#”) spawn a subshell which would interpret and run the commands contained in the file. In this model, scripts would only behave as other commands if called from within a Bourne shell. An attempt to directly execute such a file via the operating system’s own exec() system call would fail, preventing scripts from behaving uniformly as normal system commands.
1979 年 第 7 版 Unix 文档[28] 中尚无解释器指令,但对 shell 脚本的支持已显现,其描述了 Bourne shell 的一项功能:对带执行权限的文件予以特殊处理,shell 会(有时根据脚本首字符如 “:” 或 “#”)生成子 shell 来解释并运行文件中的命令。在此模型下,脚本仅在 Bourne shell 内部调用时才表现如其他命令。通过操作系统自身的 exec() 系统调用直接执行此类文件会失败,使脚本无法如普通系统命令般统一表现。
Version 8 improved shell scripts
第 8 版改进了 shell 脚本
In later versions of Unix-like systems, this inconsistency was removed. Dennis Ritchie introduced kernel support for interpreter directives in January 1980, for Version 8 Unix, with the following description:[27]
在后续类 Unix 系统版本中,这一不一致被消除。Dennis Ritchie 于 1980 年 1 月为 第 8 版 Unix 引入了对解释器指令的内核支持,描述如下:[27]
From uucp Thu Jan 10 01:37:58 1980
From dmr Thu Jan 10 04:25:49 1980 remote from research
The system has been changed so that if a file being executed begins with the magic characters #! , the rest of the line is understood to be the name of an interpreter for the executed file. Previously (and in fact still) the shell did much of this job; it automatically executed itself on a text file with executable mode when the text file’s name was typed as a command. Putting the facility into the system gives the following benefits.
系统已做修改,若待执行文件以特殊字符组合#!开头,则该行剩余部分会被识别为该待执行文件对应的解释器程序名称。此前(且实际上当前依旧),该工作的大部分由 Shell 完成;当用户输入某可执行权限文本文件的名称作为命令时,Shell 会自动调用自身来执行该文本文件。将此功能内置到系统中可带来如下优势。
- It makes shell scripts more like real executable files, because they can be the subject of ‘exec.’
该机制使 Shell 脚本更接近真正的可执行文件,因为它们可以作为exec函数的执行对象。
- If you do a ‘ps’ while such a command is running, its real name appears instead of ‘sh’. Likewise, accounting is done on the basis of the real name.
当此类命令处于运行状态时,若执行ps命令,显示的将是该命令的真实名称,而非sh。同理,系统记账功能也会基于该真实名称进行统计。
- Shell scripts can be set-user-ID.[a]
Shell 脚本可被设置为用户标识(Set-User-ID)模式。[a]
- It is simpler to have alternate shells available; e.g. if you like the Berkeley csh there is no question about which shell is to interpret a file.
备用 Shell 的配置与使用流程被简化;例如,若用户偏好伯克利版 C Shell(csh),则无需再纠结应使用哪款 Shell 来解释执行目标文件。
- It will allow other interpreters to fit in more smoothly.
该机制可使其他类型的解释器更顺畅地与系统集成。
To take advantage of this wonderful opportunity, put
若要利用此项特性,需在 Shell 脚本首行的行首位置添加如下内容:
#! /bin/sh
at the left margin of the first line of your shell scripts. Blanks after ! are OK. Use a complete pathname (no search is done). At the moment the whole line is restricted to 16 characters but this limit will be raised.
!符号后的空格是允许存在的。需使用完整路径名(系统不会执行路径搜索操作)。当前该配置行的长度限制为 16 个字符,后续此限制将会放宽。
Unnamed shell script feature
未命名的 shell 脚本功能
The feature’s creator didn’t give it a name, however:[30]
该功能的创造者并未为其命名:[30]
From:"Ritchie,DennisM(Dennis) CTR "<dmr@[redacted]>
To:<[redacted]@talisman.org>
Date:Thu, 19 Nov 2009 18:37:37 -0600
Subject:RE:Whatdo-you-callyour#!line?
主题:回复:如何称呼以#!开头的代码行?
I can’t recall that we ever gave it a proper name.
我不记得我们曾给它起过一个正式的名称。
It was pretty late that it went in–I think that I got the idea from someone at one of the UCB conferences on Berkeley Unix; I may have been one of the first to actually install it, but it was an idea that I got from elsewhere.
这项机制被纳入系统的时间其实相当晚——我记得这个想法是在某次加州大学伯克利分校举办的伯克利版 Unix 相关会议上,从其他人那里听来的;我或许是最早将其实际部署到系统中的人之一,但这个创意并非由我原创。
As for the name: probably something descriptive like “hash-bang” though this has a specifically British flavor, but in any event I don’t recall particularly using a pet name for the construction.
至于名称方面:或许可以用一个描述性的说法,比如“hash-bang”,不过这个表述带有鲜明的英式英语色彩;但无论如何,我不记得我们曾专门为这种语法结构起过什么昵称。
Kernel support for interpreter directives spread to other versions of Unix, and one modern implementation can be seen in the Linux kernel source in fs/binfmt_script.c.[31]
对解释器指令的内核支持扩散到其他 Unix 版本,现代实现可见于 Linux 内核源码 fs/binfmt_script.c。[31]
This mechanism allows scripts to be used in virtually any context normal compiled programs can be, including as full system programs, and even as interpreters of other scripts. As a caveat, though, some early versions of kernel support limited the length of the interpreter directive to roughly 32 characters (just 16 in its first implementation), would fail to split the interpreter name from any parameters in the directive, or had other quirks. Additionally, some modern systems allow the entire mechanism to be constrained or disabled for security purposes (for example, set-user-id support has been disabled for scripts on many systems).
该机制使脚本几乎可在任何普通编译程序可用的上下文中使用,包括作为完整系统程序,甚至作为其他脚本的解释器。但需注意,某些早期内核支持版本将解释器指令长度限制在大约 32 字符(首次实现仅 16 字符),无法把解释器名称与指令中的参数拆分,或存在其他怪癖。此外,某些现代系统出于安全目的允许对整个机制加以限制或禁用(例如,许多系统已禁用脚本的 set-user-id 支持)。
Note that, even in systems with full kernel support for the #! magic number, some scripts lacking interpreter directives (although usually still requiring execute permission) are still runnable by virtue of the legacy script handling of the Bourne shell, still present in many of its modern descendants. Scripts are then interpreted by the user’s default shell.
请注意,即使在完整支持 #! 魔数 的系统中,某些缺少解释器指令的脚本(通常仍需执行权限)仍可凭借 Bourne shell 的遗留脚本处理功能运行,该功能仍存在于其许多现代后代中。此时脚本由用户的默认 shell 解释。
Notes
注释
- The setuid feature is disabled in most modern operating systems following the realization that a race condition can be exploited to change the script while it is being processed.[29]
鉴于存在可利用的竞态条件在脚本处理过程中篡改脚本,大多数现代操作系统已禁用 setuid 功能。[29]
References
-
“Advanced Bash Scripting Guide: Chapter 2. Starting Off With a Sha-Bang”. Archived from the original on 10 December 2019. Retrieved 10 December 2019.
-
Cooper, Mendel (5 November 2010). Advanced Bash Scripting Guide 5.3 Volume 1. lulu.com. p. 5. [ISBN] 978-1-4357-5218-4.
-
MacDonald, Matthew (2011). HTML5: The Missing Manual. Sebastopol, California: [O’Reilly Media]. p. 373. [ISBN] 978-1-4493-0239-9.
-
Lutz, Mark (September 2009). Learning Python (4th ed.). O’Reilly Media. p. 48. ISBN 978-0-596-15806-4.
-
Guelich, Gundavaram and Birznieks, Scott, Shishir and Gunther (29 July 2000). CGI Programming with PERL (2nd ed.). [O’Reilly Media] p. 358. [ISBN] 978-1-56592-419-2.
{{cite book}}: CS1 maint: multiple names: authors list (link) -
Lie Hetland, Magnus (4 October 2005). Beginning Python: From Novice to Professional. Apress. p. 21. [ISBN] 978-1-59059-519-0.
-
Schitka, John (24 December 2002). Linux+ Guide to Linux Certification. Course Technology. p. 353. [ISBN] 978-0-619-13004-6.
-
[a][b] “execve(2) - Linux man page”. Retrieved 21 October 2010.
-
[a][b] Maschek, Sven (30 December 2010). “The #! magic, details about the shebang/hash-bang mechanism: Blank after #! required?”. www.in-ulm.de. Retrieved 18 January 2024.
-
if permitted by the file’s exec permission bits
-
“The Open Group Base Specifications Issue 7”. 2008. Retrieved 5 April 2010.
-
“pixelbeat.org: Common shell script mistakes”. It’s much better to test scripts directly in a POSIX compliant shell if possible. The
bash --posixoption doesn’t suffice as it still accepts some ‘bashisms’ -
“Chapter 2. Shell Command Language”, The Open Group Base Specifications (IEEE Std 1003.1-2017) (Issue 7 ed.), IEEE, 2018 [2008], If the first line of a file of shell commands starts with the characters “#!”, the results are unspecified
-
Autoconf, Free Software Foundation, Macro: AC_SYS_INTERPRETER: Check whether the system supports starting scripts with a line of the form ‘#!/bin/sh’ to select the interpreter to use for the script.
-
“What’s with #!/usr/bin/env bash?”. Retrieved 6 March 2024.
-
“execve(2) man page”., section “Interpreter scripts”
-
“/usr/bin/env behaviour”. Mail-index.netbsd.org. 9 November 2008. Retrieved 18 November 2010.
-
env(1)– FreeBSD General Commands Manual -
“env invocation”. GNU Coreutils. Retrieved 11 February 2020.
-
“Carriage Return causes bash to fail”. 8 November 2013.
-
“GNU Autoconf Manual v2.57, Chapter 10: Portable Shell Programming”. Archived from the original on 18 January 2008. Retrieved 14 May 2020.
-
[a][b] “FAQ UTF-8, UTF-16, UTF-32 & BOM: Can a UTF-8 data stream contain the BOM character (in UTF-8 form)? If yes, then can I still assume the remaining UTF-8 bytes are in big-endian order?”. Unicode. Retrieved 10 November 2023.
-
“Jargon File entry for shebang”. Catb.org. Retrieved 16 June 2010.
-
Wall, Larry. “Perl didn’t grok setuid scripts that had a space on the first line between the shebang and the interpreter name”. USENET.
-
[a][b] “CSRG Archive CD-ROMs”.
-
UNIX TIME-SHARING SYSTEM: UNIX PROGRAMMER’S MANUAL (PDF), vol. 2A (Seventh ed.), January 1979
-
Gilles. “linux - Why is SUID disabled for shell scripts but not for binaries?”. Information Security Stack Exchange.
-
Richie, Dennis. “Dennis Ritchie and Hash-Bang”. Talisman.org. Retrieved 3 December 2020.
-
Rubini, Alessandro (31 December 1997). “Playing with Binary Formats”. Linux Journal. Retrieved 1 January 2015.
via:
- Understanding Unix Shebang: History & Implementation
https://tossthecoin.tcl.com/blog/understanding-unix-shebang-history-and - Bash Shebang: The Ultimate Guide to Script Interpreters — linuxvox.com
https://linuxvox.com/blog/bash-shebang/ - Shebang (Unix) Explained_
https://www.detailedpedia.com/wiki-Shebang_(Unix)
711

被折叠的 条评论
为什么被折叠?



