Answers:
这与您需要编写类似的原因:
rm -- *.txt
并不是
rm *.txt
除非可以保证.txt
当前目录中的所有文件都没有以-
。开头的名称。
在:
rm <arg>
<arg>
如果以开头,则视为选项,-
否则将其删除。在
rm- <arg>
arg
不论文件是否开头,始终将其视为要删除的文件-
。
相同sh
。
当执行一个以
#! /bin/sh
通常使用:
execve("path/to/the-script", ["the-script", "arg"], [environ])
系统将其转换为:
execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])
在path/to/the-script
通常不是东西,是脚本作者的控制之下。作者无法预测脚本副本的存储位置和名称。特别是,他们不能保证path/to/the-script
调用它的开头不会-
(或+
也是一个问题sh
)。这就是为什么我们需要在-
这里标记选项的结束。
例如,在我的系统上zcat
(实际上与大多数其他脚本一样)是一个不遵循该建议的脚本示例:
$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]
现在您可能会问为什么#! /bin/sh -
而不是#! /bin/sh --
?
尽管#! /bin/sh --
可以与POSIX shell一起使用,但它#! /bin/sh -
具有更高的可移植性。特别是古代版本的sh
。sh
将-
选项终止日期和终止日期视为早getopt()
,并且普遍使用--
来长时间标记选项终止。Bourne shell(从70年代后期开始)解析其参数的方式,如果以开头,则仅考虑第一个参数作为选项-
。之后的所有字符-
将被视为选项名称;如果后面没有字符-
,则没有选项。这样就卡住了,后来所有类似Bourne的Shell都被-
视为标记选项结束的一种方式。
在Bourne外壳程序中(而不是在现代的Bourne外壳程序中),#! /bin/sh -euf
该问题也可以解决,因为只考虑第一个参数作为选项。
现在,有人可以说我们在这里要学脚,这也是为什么我在上面的斜体字中写了这个需求:
-
或开头的脚本调用脚本,+
或者将脚本放在以-
或开头的目录中+
。execvp()
/ execlp()
-type函数。在那种情况下,通常您the-script
会根据要查找的内容来调用它们,$PATH
在这种情况下,execve()
系统调用的path参数通常以/
(not -
或+
)开头,或者./the-script
好像您希望the-script
在当前目录中运行(并且那么路径的开头./
,而不是-
也不+
要么)。现在,除了理论上的正确性问题外,还有另一个原因#! /bin/sh -
被推荐为优良作法。这可以追溯到几个系统仍支持setuid脚本的时代。
如果您的脚本包含:
#! /bin/sh
/bin/echo "I'm running as root"
-r-sr-xr-x root bin
在那些系统上,当该脚本由普通用户执行时,该脚本是setuid根目录(与许可一样)。
execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])
将完成root
!
如果用户创建了一个符号链接/tmp/-i -> path/to/the-script
并以符号身份执行-i
,它将以形式启动交互式shell(/bin/sh -i
)root
。
该-
会的工作围绕(它不会解决的竞争条件的问题,或事实,一些sh
像一些实现ksh88
基于那些将会查找脚本参数不/
中$PATH
虽然)。
如今,几乎所有系统都不再支持setuid脚本,而那些仍然支持setuid脚本(通常不是默认情况)的最终结果是execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])
(<n>
解决了此问题和比赛条件。
请注意,大多数解释器都会遇到类似的问题,而不仅仅是像Bourne这样的shell。非伯恩类外壳通常不支持-
作为选项结束标记,但通常支持--
(至少对于现代版本而言)。
也
#! /usr/bin/awk -f
#! /usr/bin/sed -f
没有问题作为下一个参数被认为是一个参数传递给-f
在任何情况下的选择,但如果它仍然无法正常工作path/to/script
是-
(在这种情况下,与大多数sed
/ awk
实现,sed
/ awk
不读从代码-
文件,但来自stdin)。
还要注意,在大多数系统上,不能使用:
#! /usr/bin/env sh -
#! /usr/bin/perl -w --
与大多数系统一样,shebang机制在解释器路径之后仅允许一个参数。
至于是否使用#! /bin/sh -
vs #!/bin/sh -
,这只是个问题。我更喜欢前者,因为它可以使解释器路径更加可见,并使鼠标选择更加容易。有传说说在某些古老的Unix版本中需要该空间,但AFAIK尚未得到验证。
关于Unix shebang的很好的参考可以在https://www.in-ulm.de/~mascheck/various/shebang中找到。
bash
可以接受其他所有shell一样的选项。作为Bourne风格的POSIX外壳,bash
接受#! /bin/bash -
和#! /bin/bash --
。