有没有办法动态选择正在执行脚本的解释器?我有一个在两个不同系统上运行的脚本,并且我想要使用的解释器位于两个系统上的不同位置。我最终不得不做的是每次切换时更改 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" "$@"
将包装器保存在用户的PATH
as中program
,并将实际程序放在一边或使用其他名称。
我#!/bin/bash
在hashbang中使用是因为数组flags
。如果您不需要存储可变数量的标志等并且可以不需要它,则该脚本应该可移植地与#!/bin/sh
.
答案2
不,那行不通。这两个字符#!
绝对需要是文件中的前两个字符(您如何指定解释 if 语句的内容?)。这构成了exec()
函数系列在确定要执行的文件是脚本(需要解释器)还是二进制文件(不需要解释器)时检测到的“幻数” 。
shebang 行的格式相当严格。它需要有一条通往解释器的绝对路径,并且最多有一个参数。
你什么能要做的是使用env
:
#!/usr/bin/env interpreter
现在,通往的路径env
是通常 /usr/bin/env
,但从技术上讲,这并不能保证。
这允许您调整PATH
每个系统上的环境变量,以便找到interpreter
(无论是它bash
,python
还是您拥有的任何东西)。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 等。这也保留了将参数传递给脚本的能力。