shebang 中的多个参数

shebang 中的多个参数

我想知道是否有一种通用方法可以通过 shebang 行 ( #!) 将多个选项传递给可执行文件。

我使用 NixOS,我编写的任何脚本中 shebang 的第一部分通常是/usr/bin/env.我遇到的问题是,后面的所有内容都被系统解释为单个文件或目录。

例如,假设我想编写一个bash在 posix 模式下执行的脚本。编写 shebang 的简单方式是:

#!/usr/bin/env bash --posix

但尝试执行生成的脚本会产生以下错误:

/usr/bin/env: ‘bash --posix’: No such file or directory

我知道这个帖子,但我想知道是否有更通用和更干净的解决方案。


编辑: 我知道对于诡计脚本,有一种方法可以实现我想要的,记录在第 4.3.4 节手册的:

 #!/usr/bin/env sh
 exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
 !#

这里的技巧是,第二行(以 开头exec)被 but 解释为代码sh,位于#!...!#块中,作为注释,因此被 Guile 解释器忽略。

难道不能将此方法推广到任何解释器吗?


第二次编辑:经过一番尝试后,对于可以从 读取输入的解释器来说stdin,以下方法似乎可行:

#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;

不过,这可能不是最佳的,因为该sh过程会一直持续到解释器完成其工作为止。任何反馈或建议将不胜感激。

答案1

虽然不完全可移植,但从 coreutils 8.30 开始根据其文档您将能够使用:

#!/usr/bin/env -S command arg1 arg2 ...

所以给出:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

你会得到:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

如果你好奇的showargs是:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

答案2

没有通用的解决方案,至少如果你需要支持Linux则没有,因为Linux 内核将 shebang 行中第一个“单词”之后的所有内容视为单个参数

我不确定 NixOS 的限制是什么,但通常我会把你的 shebang 写为

#!/bin/bash --posix

或者,在可能的情况下,在脚本中设置选项

set -o posix

或者,您可以使用适当的 shell 调用让脚本自行重新启动:

#!/bin/sh -

if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

shift

# Processing continues

这种方法可以推广到其他语言,只要您找到一种方法让目标语言忽略前几行(由 shell 解释)。

GNU coreutils'env自版本 8.30 起提供了解决方法,请参阅乌节点回答了解详情。 (这在 Debian 10 及更高版本、RHEL 8 及更高版本、Ubuntu 19.04 及更高版本等中可用)

答案3

POSIX 标准的描述非常简洁#!

来自exec()系统接口系列文档的基本原理部分

一些历史实现处理 shell 脚本的另一种方法是将文件的前两个字节识别为字符串#!,并使用文件第一行的其余部分作为要执行的命令解释器的名称。

来自外壳介绍部分

shell 从文件(请参阅 参考资料sh)、-c选项或POSIX.1-2008 系统接口卷中定义的system()和函数读取其输入。popen()如果 shell 命令文件的第一行以字符开头#!,则结果未指定

这基本上意味着任何实现(您正在使用的 Unix)都可以根据需要自由地执行 shebang 行的解析细节。

一些 Unices,如 macOS(无法测试 ATM),会将 shebang 行上提供给解释器的参数拆分为单独的参数,而 Linux 和大多数其他 Unices 会将参数作为单个选项提供给解释器。

因此,依赖 shebang 线能够接受多个参数是不明智的。

另请参阅维基百科上 Shebang 文章的可移植性部分


一种简单的解决方案可以推广到任何实用程序或语言,即制作一个包装脚本,使用适当的命令行参数执行实际脚本:

#!/bin/sh
exec /bin/bash --posix /some/path/realscript "$@"

我不认为我个人会尝试让它重新执行本身因为这感觉有点脆弱。

答案4

shebang 描述于execve(2)手册页如下:

#! interpreter [optional-arg]

此语法接受两个空格:

  1. 前面一个空格口译员路径,但这个空格是可选的。
  2. 一个空格分隔口译员路径及其可选参数。

请注意,在谈论可选参数时我没有使用复数,上面的语法也没有使用[optional-arg ...],如您最多可以提供一个参数

就 shell 脚本而言,您可以set在脚本开头附近使用内置命令,这将允许设置解释器参数,提供与使用命令行参数相同的结果。

在你的情况下:

set -o posix

从 Bash 提示符中,检查 的输出help set以获取所有可用选项。

相关内容