脚本启动后选择解释器,例如 hashbang 内的 if/else

脚本启动后选择解释器,例如 hashbang 内的 if/else

有没有办法动态选择正在执行脚本的解释器?我有一个在两个不同系统上运行的脚本,并且我想要使用的解释器位于两个系统上的不同位置。我最终不得不做的是每次切换时更改 hashbang 行。我想做的事情就是逻辑相当于这个(我意识到这个精确的构造是不可能的):

if running on system A:
    #!/path/to/python/on/systemA
elif running on system B:
    #!/path/on/systemB

#Rest of script goes here

或者更好的是这样,它尝试使用第一个解释器,如果找不到,则使用第二个:

try:
    #!/path/to/python/on/systemA
except: 
    #!path/on/systemB

#Rest of script goes here

显然,我可以根据我所在的位置执行它 /path/to/python/on/systemA myscript.py/path/on/systemB myscript.py 但实际上我有一个启动的包装脚本myscript.py,所以我想以编程方式而不是手动指定python解释器的路径。

答案1

您始终可以创建一个包装脚本来为实际程序找到正确的解释器:

#!/bin/bash
if something ; then
    interpreter=this
    script=/some/path/to/program.real
    flags=()
else
    interpreter=that
    script=/other/path/to/program.real
    flags=(-x -y)
fi
exec "$interpreter" "${flags[@]}" "$script" "$@"

将包装器保存在用户的PATHas中program,并将实际程序放在一边或使用其他名称。

#!/bin/bash在hashbang中使用是因为数组flags。如果您不需要存储可变数量的标志等并且可以不需要它,则该脚本应该可移植地与#!/bin/sh.

答案2

不,那行不通。这两个字符#!绝对需要是文件中的前两个字符(您如何指定解释 if 语句的内容?)。这构成了exec()函数系列在确定要执行的文件是脚本(需要解释器)还是二进制文件(不需要解释器)时检测到的“幻数” 。

shebang 行的格式相当严格。它需要有一条通往解释器的绝对路径,并且最多有一个参数。

你什么要做的是使用env

#!/usr/bin/env interpreter

现在,通往的路径env通常 /usr/bin/env,但从技术上讲,这并不能保证。

这允许您调整PATH每个系统上的环境变量,以便找到interpreter(无论是它bashpython还是您拥有的任何东西)。perl

这种方法的缺点是不可能将参数可移植地传递给解释器。

这意味着

#!/usr/bin/env awk -f

#!/usr/bin/env sed -f

不太可能在某些系统上工作。

另一个明显的方法是使用 GNU 自动工具(或一些更简单的模板系统)来查找解释器并在步骤中将正确的路径放入文件中./configure,该步骤将在每个系统上安装脚本时运行。

人们还可以诉诸于使用显式解释器运行脚本,但这显然是您想要避免的:

$ sed -f script.sed

答案3

您还可以编写多语言(组合两种语言)。 /bin/sh 保证存在。

这样做的缺点是代码丑陋。但当env它不存在或存在于 /usr/bin/env 之外的其他位置时,可以使用它。如果您想做一些非常奇特的选择,也可以使用它。

脚本的第一部分确定在使用 /bin/sh 作为解释器运行时使用哪个解释器,但在由正确的解释器运行时会被忽略。我们用来exec防止 shell 运行超过第一部分。

Python 示例:

#! /bin/sh -
''':'
# Python thinks this is a string, docstring unfortunately.
# The shell just ran the no-op : command.
find_best_python ()
{
    for candidate in pypy3 pypy python3 python; do
        if "$candidate" -c '' 2>/dev/null; then
            echo "$candidate"
            return
        fi
    done
    echo >&2 "Can't find any Python"
    false
}
interpreter="$(find_best_python)" || exit  # Replace with something fancier.
# Re-run the rest of the script with the chosen interpreter:
exec "$interpreter" "$0" "$@"
'''

print("Python code goes here")

答案4

虽然这不会选择 shell 脚本中的解释器(它会为每台计算机选择解释器),但如果您对尝试运行脚本的所有计算机具有管理访问权限,那么这是一个更简单的选择。

创建一个符号链接(或硬链接,如果需要)以指向所需的解释器路径。例如,在我的系统上,perl 和 python 位于 /usr/bin 中:

cd /bin
ln -s /usr/bin/perl perl
ln -s /usr/bin/python python

将创建一个符号链接以允许 hashbang 解析 /bin/perl 等。这也保留了将参数传递给脚本的能力。

相关内容