在POSIX规范的Shell命令语言页面中:
如果shell命令文件的第一行以字符“#!”开头,则结果不确定。
为什么#!
POSIX无法指定行为?我感到困惑的是,如此便携式和广泛使用的东西将具有未指定的行为。
exec()
功能的库解释的。因此,检查多个外壳并不能真正告诉您它的可移植性。
在POSIX规范的Shell命令语言页面中:
如果shell命令文件的第一行以字符“#!”开头,则结果不确定。
为什么#!
POSIX无法指定行为?我感到困惑的是,如此便携式和广泛使用的东西将具有未指定的行为。
exec()
功能的库解释的。因此,检查多个外壳并不能真正告诉您它的可移植性。
Answers:
我认为主要是因为:
实施之间的行为差异很大。有关所有详细信息,请参见https://www.in-ulm.de/~mascheck/various/shebang/。
但是,它现在可以指定大多数类似Unix的实现的最小子集:例如#! *[^ ]+( +[^ ]+)?\n
(只有可移植文件名字符集中的字符位于这两个词中),其中第一个词是本机可执行文件的绝对路径,而事实并非如此时间太长,如果可执行文件是setuid / setgid,则行为未指定,实现定义了将解释器路径还是脚本路径传递argv[0]
给了解释器。
POSIX始终不指定可执行文件的路径。几个系统在/bin
/中/usr/bin
具有POSIX 之前的实用程序,而在其他地方也具有POSIX实用程序(例如在Solaris 10上/bin/sh
是Bourne shell,而POSIX则在其中/usr/xpg4/bin
; Solaris 11用ksh93替换了它,它更符合POSIX,但其他大多数工具/bin
仍然是古老的非POSIX 工具)。某些系统不是POSIX的,但是具有POSIX模式/仿真。POSIX所要求的只是在一个有文档记录的环境中,系统在该环境中表现为POSIXly。
例如,参见Windows + Cygwin。实际上,在Windows + Cygwin中,当由cygwin应用程序而不是由本机Windows应用程序调用脚本时,会放任自流。
因此,即使POSIX指定它不能被用来写家当机制POSIX sh
/ sed
/ awk
...脚本(也注意到家当机制不能被用来编写可靠sed
/ awk
脚本,因为它不允许传递一个最终的选项标记)。
现在,未指定它的事实并不意味着您无法使用它(嗯,它表示#!
如果您希望它只是常规注释而不是爆炸的话,则不应以第一行开头),但是POSIX不能保证您这样做。
以我的经验,使用shebangs比使用POSIX编写Shell脚本的方式为您提供了更多的可移植性保证:不用担心,用POSIX sh
语法编写脚本,并希望任何调用脚本的人都可以调用sh
与之兼容的POSIX 。如果您知道脚本将在正确的环境中由正确的工具调用,则可以,否则可以。
您可能需要执行以下操作:
#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
# cover Solaris 10 or older. ": ^ false" returns false
# in the Bourne shell as ^ is an alias for | there for
# compatibility with the Thomson shell.
PATH=`getconf PATH`:$PATH; export PATH
exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script
如果要移植到Windows + Cygwin,则可能必须用.bat
或.ps1
扩展名命名文件,并使用类似的技巧cmd.exe
或者在同一文件上powershell.exe
调用cygwin sh
。
sh
,它不需要hashbang行,因为它将由POSIX执行sh
。
execlp
或时execvp
才是对的,对吗?如果使用execve
,会导致ENOEXEC吗?
在所有POSIX投诉外壳之间,行为似乎是一致的。我认为这里不需要摆动空间。
您看起来不够深入。
早在1980年代,这种机制实际上还没有标准化。尽管丹尼斯·里奇(Dennis Ritchie)实施了该实现,但该实现尚未在宇宙AT&T方面向公众公开。实际上,它仅在BSD中公开可用并已知;带有在AT&T Unix上不可用的可执行Shell脚本。因此,对其进行标准化是不合理的。这种当代的doco就是事态的例子,其中之一是:
请注意,BSD允许以开始的文件斯蒂芬·弗雷德(1988)。“在系统X版本Y上编程”。澳大利亚Unix系统用户组通讯。第9卷第4期。111。#! interpreter
直接执行,而SysV只允许a.out的文件直接执行。这意味着exec…()
可能必须在SysV下更改BSD程序中例程之一的实例,以/bin/sh
代替执行该程序的解释器(通常是)。
这里重要的一点是您正在查看shell,而可执行shell脚本的存在实际上是exec…()
功能的问题。Shell所做的事情包括可执行脚本机制的前身,即使在今天(以及今天,对于exec…p()
功能子集的要求)中,仍然可以在某些Shell中找到它们,并且有些误导。在这方面,标准需要解决的是如何exec…()
在解释的脚本上工作,而在最初创建POSIX时,它根本就没有在目标操作系统的主要部分起作用。
一个从属问题是为什么自从1990年代之初以来,这一直未标准化,特别是因为脚本解释器的魔术数字机制已经在宇宙的AT&T端公开并exec…()
在System 5接口定义中得到了证明。:
解释器文件以以下形式的一行开头—#!路径名[arg]其中pathname是解释器的路径,而arg是可选参数。当您exec
使用解释器文件时,系统exec
将使用指定的解释器。
exec
。 系统V接口定义。第1卷,1991年。
不幸的是,今天的行为仍然和1980年代一样广泛地存在分歧,并且没有真正常见的行为可以标准化。某些Unices(例如著名的HP-UX和FreeBSD)不支持将脚本用作脚本的解释器。第一行是由空格分隔的一个,两个还是多个元素,在MacOS(以及2005之前的FreeBSD版本)之间以及其他之间有所不同。支持的最大路径长度会有所不同。 ␀
POSIX可移植文件名字符集之外的字符以及前导和尾随空格都非常棘手。第0,第1和第2参数的结果也是棘手的,整个系统之间存在很大差异。一些当前符合POSIX要求,但不符合-Unix系统仍然不支持任何这种机制,并且强制将其转换为不再符合POSIX的机制。
script
。 NetBSD杂项信息手册。2005-05-06。正如其他一些答案所指出的,实现方式也有所不同。这使得很难标准化和保持与现有脚本的向后兼容性。即使对于现代POSIX系统也是如此。例如,Linux并没有完全用空格标记shebang行。macOS不允许脚本解释器为另一个脚本。