进一步阅读

进一步阅读

我想将 shebang 放入#!/bin/sh -eufo pipefail我的脚本中。但有几个地方很奇怪:

  1. 该脚本在 FreeBSD 中会因该 shebang 而失败,但在 MacOS 上运行时不会失败
  2. 在 FreeBSd 上,当直接从命令行执行时,相同的 shebang 可以工作(也/bin/sh)。
>>> sh -eufo pipefail -c 'echo hi'  # this works
hi

>>> cat <<EOF > script                                                                                      
#!/bin/sh -eufo pipefail
echo hi
EOF

>>> chmod +x ./script 
>>> ./script  # this doesn't work on FreeBSD but works on MacOS
Illegal option -o ./script

>>> cat ./script 
#!/bin/sh -eufo pipefail
echo hi

>>> uname -a
FreeBS 11.3-RELEASE-p7

答案1

#!MacOS 仍然保留了 2005 年之前旧的 FreeBSD 行为。2005 年,FreeBSD 内核在传递给 的可执行文件的开头处理的方式发生了重大变化execve(),以使其与其他一些操作系统内核更加一致,包括 Linux 和 NetBSD 内核。

NetBSD 内核源代码中的注释试图将此描绘为通用的:

* 收集 shell 参数。 shell 名称后面的所有内容
 * 作为一个参数传递;这是正确的(历史)
 * 行为。
—— kern/exec-script.c。网络BSD。第 189 行及以下。

事实上并非如此。 Sven Mascheck 大约十年前做了一些测试,有就基本行为而言,AT&T Unix System 5 与 4.2BSD 一样具有“正确的历史”行为:

  • 忽略字符(4.2BSD 和 AT&T Unix System 5 之前)。
  • 在单个参数中传递整个字符串(从 2005 年起,4.2BSD、NetBSD、Linux 和 FreeBSD)。
  • 用空格分割字符串并将其作为多个参数传递(2005 年之前的 FreeBSD 和 MacOS)。
  • 用空格分割字符串并仅传递第一个参数(AT&T Unix System 5 和 Solaris)

我仅在括号中包含与此答案相关的操作系统。 M. Mascheck 检查了更多内容,就像 Ahmon Dancy 在 FreeBSD 问题报告 16393 的讨论中所做的那样。有关完整列表,请参阅进一步阅读。

具有讽刺意味的是,2005 年 FreeBSD 的关键在于,FreeBSD 并没有相当就如此容易。它引入了一项更改,旨在使有关 Perl 的流行书籍中所写的内容真正起作用:在注释字符后跳过参数。这些书推荐了以下内容:

#!/bin/sh -- # -*- perl -*-
— 拉里·沃尔、汤姆·克里斯蒂安森、乔恩·奥尔旺特 (2000)。Perl 编程:第三版。奥莱利媒体。 ISBN 9780596000271。p。 488.

2000 年的 PR 16393 是一种让内核处理可执行 Perl 脚本的方法,其编写方式正是 Larry Wall 所说的可行的。然而,它破坏了其他东西并且没有完全起作用。

在这件事上有一些来来回回。最后,在 2005 年,使 Larry Wall 等人的想法发挥作用的机制被移出内核,该机制被设计为与 Linux、NetBSD 和 4.2BSD(而不是 Solaris 和 AT&T Unix System 5)兼容,并使得的责任sh

因此,自 2005 年以来的行为是 shell 获取三个参数,第二个参数是该#!行的整个尾部,直接使用调用脚本execve()实际上与调用相同:

sh '-eufo pipelinefail' ./script

很明显为什么 Almquist shell(shFreeBSD 上的)认为这./script是该选项的选项参数-o,并且它将该pipefail部分视为后面收集的进一步的单字母选项-(它没有得到这个选项)。尚未处理)。

另一个明显的替代方案是set -o pipefail作为脚本中的第一个命令,如所指出的https://unix.stackexchange.com/a/533418/5132为了谍影重重壳。这仅被添加到 FreeBSD 中阿尔姆奎斯特然而,shell 是在 2019 年推出的,因此仅在最新版本的 FreeBSD 中可用。 (这德班截至 2020 年,Almquist shell 尚未添加它。)

进一步阅读

答案2

即使您的#!用法是不可移植的,因为它使用的不仅仅是简单的:

#!/bin/sh

或普遍支持的单一参数,如下所示:

#!/bin/sh -oneflag

真正的问题是您使用的是不可移植的选项-o pipefail

ksh93这是其他 shell不bash支持的选项。

背景:

  • 在 MacOS 上,/bin/shbash已以特定方式编译(例如,echo默认情况下使转义序列起作用)以使其符合 POSIX 标准。由于 bash 支持pipefail(见上文),因此它可以在 MacOS 上运行。

  • 在 FreeBSD 上,/bin/shash支持pipefail.

你有两种可能的方法:

  • 不使用set -o pipefail

  • 等两年再试试。这很可能会起作用,因为我们决定在 10 个月前将此选项添加到下一个 POSIX 标准(第 8 期)中,请参阅https://www.austingroupbugs.net/view.php?id=789由于下一个 POSIX 标准将在大约 2019 年发布。一年后,FreeBSD 很有可能很快就会添加set -o pipefail支持ash

相关内容