shebang中的多个参数

Ras*_*los 68 scripting environment-variables posix arguments shebang

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

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

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

#!/usr/bin/env bash --posix
Run Code Online (Sandbox Code Playgroud)

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

/usr/bin/env: ‘bash --posix’: No such file or directory
Run Code Online (Sandbox Code Playgroud)

我知道这篇文章,但我想知道是否有更通用和更清洁的解决方案。


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

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

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

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


第二次编辑:在玩了一会儿之后,似乎对于可以从中读取输入的解释器,stdin以下方法可以工作:

#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;
Run Code Online (Sandbox Code Playgroud)

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

uno*_*ode 60

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

#!/usr/bin/env -S command arg1 arg2 ...
Run Code Online (Sandbox Code Playgroud)

所以给出:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too
Run Code Online (Sandbox Code Playgroud)

你会得到:

% ./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'
Run Code Online (Sandbox Code Playgroud)

万一你好奇的showargs是:

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

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done
Run Code Online (Sandbox Code Playgroud)

  • @Kokizzu 第一个包含 coreutils 8.30 的 Ubuntu 系列是 19.04。这就是它在 18.04 上不起作用的原因。 (3认同)
  • 该选项是从 FreeBSD 的 `env` 复制而来的,其中 `-S` 是在 2005 年添加的。参见 https://lists.gnu.org/r/coreutils/2018-04/msg00011.html (2认同)
  • 仅供参考:从 coreutils 8.31 开始,`env` 包含自己的 `showargs`:[-v 选项](https://www.gnu.org/software/coreutils/manual/html_node/env-inspiration.html#Testing-和故障排除)例如 `#!/usr/bin/env -vS --option1 --option2 ...` (2认同)
  • 不适用于 ubuntu 18.04.3 (2认同)

Ste*_*itt 43

没有通用的解决方案,至少在您需要支持 Linux 时不会,因为Linux 内核将 shebang 行中第一个“单词”之后的所有内容都视为单个参数

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

#!/bin/bash --posix
Run Code Online (Sandbox Code Playgroud)

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

set -o posix
Run Code Online (Sandbox Code Playgroud)

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

#!/bin/sh -

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

shift

# Processing continues
Run Code Online (Sandbox Code Playgroud)

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

GNU coreutils'env自 8.30 版起提供了一种解决方法,有关详细信息,请参阅unode答案。(这在 Debian 10 及更高版本、RHEL 8 及更高版本、Ubuntu 19.04 及更高版本等中可用)

  • @Szczepan 如果您有解决问题的方法,请随时写下您自己的答案。 (2认同)

Kus*_*nda 14

POSIX 标准在描述方面非常简洁#!

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

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

外壳介绍部分

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

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

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

因此,依靠 shebang 行能够接受多个论点是不明智的。

另请参阅Wikipedia 上 Shebang 文章可移植性部分


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

#!/bin/sh
exec /bin/bash --posix /some/path/realscript "$@"
Run Code Online (Sandbox Code Playgroud)

我不认为我会亲自尝试,使其重新执行本身的那种感觉有点脆弱。


Whi*_*olf 9

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

#! interpreter [optional-arg]
Run Code Online (Sandbox Code Playgroud)

此语法中接受两个空格:

  1. 解释器路径前一个空格,但这个空格是可选的。
  2. 一个空格分隔解释器路径及其可选参数。

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

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

在你的情况下:

set -o posix
Run Code Online (Sandbox Code Playgroud)

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


til*_*ill 5

在寻找接受脚本作为单个参数的可执行文件时,我发现了一个相当愚蠢的解决方法:

#!/usr/bin/awk BEGIN{system("bash --posix "ARGV[1])}
Run Code Online (Sandbox Code Playgroud)