我尝试构建可以随处运行的脚本。为此,我使用了一个自定义构建的python,它始终位于相对于脚本的父目录中。
通过这种方式,我可以将我的软件包加载到USB记忆棒上,无论硬盘安装在何处以及是否安装了python,它都可以在任何地方使用。
然而,当我使用
时#!../python
然后它只在从其目录调用脚本时才有效,这当然是不可接受的。
有没有办法做到这一点,或者在目前的shebang机制中这是不可能的?
答案 0 :(得分:15)
this page有许多健康的多行shebang脚本用于很多语言,例如:
#!/bin/sh
"exec" "`dirname $0`/python" "$0" "$@"
print copyright
如果你想要一行shebang,this answer(和问题)在细节中解释了这个问题,并建议在shebang中使用其他脚本的以下方法:
#!/usr/bin/awk BEGIN{a=ARGV[1];sub(/[a-z_.]+$/,"python",a);system(a"\t"ARGV[1])}
#!/usr/bin/perl -e$_=$ARGV[0];exec(s/\w+$/python/r,$_)
答案 1 :(得分:4)
扩展@Anton的答案,以免在路径上的空格和其他特殊字符上失败,并进一步解释魔术。
| date | city_1 | city_2 | ... | city_100 |
+-----------+------------+------------+-------+-----------+
| 20.02.2018| london | india | ... | canada |
| 21.02.2018| newyork | srilanka | ... | austria |
| ... | ... | ... | ... | ... |
sh和python都可以理解这个聪明的脚本。每个人对它的反应都不同。 shebang之后的前两行由sh解释,并使其将exec移交给相对的python二进制文件(提供相同的命令行参数)。这些相同的行被python安全地丢弃,因为它们等于一个字符串(“ true”),然后是一个多行字符串(''')。
有关该主题的更多信息,请阅读here。
答案 2 :(得分:1)
对于在寻找python hashbang行的可移植解决方案时发现此问题的其他人,并且发现上面的AWK命令不能用于传递多个参数,请使用以下代码。
#!/usr/bin/awk BEGIN{a=ARGV[1];b="";for(i=1;i<ARGC;i++){b=b" \""ARGV[i]"\"";}sub(/[a-z_.\-]+$/,"python",a);system(a"\t"b)}
要更改当前目录中的所有脚本hashbang行,您可以执行以下操作。
sed -i '1 s|^#!.*|#!/usr/bin/awk BEGIN{a=ARGV[1];b="";for(i=1;i<ARGC;i++){b=b" \""ARGV[i]"\"";}sub(/[a-z_.\-]+$/,"python3.5",a);system(a"\\t"b)}|' *
答案 3 :(得分:1)
这里是一个shebang,它将传递参数,使用名称中包含非单词字符的脚本,以及使用旧版本的perl(未添加非破坏性替换标志perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LC_ALL = (unset),
LC_CTYPE = "en",
LC_NUMERIC = "C",
LANG = "en_GB.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Can't find string terminator "'" anywhere before EOF at -e line 1.
直到5.13.2)。接受的答案中的perl示例仅将脚本本身作为参数传递,而不是所有带有/r
的参数。
@ARGV
此shebang运行perl来运行内联脚本#!/usr/bin/perl -e$_=$ARGV[0];s/[^\/]+$/python/;exec($_,@ARGV)
。它将脚本-e
的路径分配给默认变量$ARGV[0]
。然后,对默认变量执行perl替换$_
,以替换路径末尾(锚定为s/[^\/]+$/python/
的所有非正斜杠/
字符([^\/]+
)以及要运行的备用命令的名称($
)。最后,它使用python
运行新命令,并将原始调用的所有参数传递给它(exec($_
)
答案 4 :(得分:0)
在查看了这些答案之后,我决定使用特定于Python的解决方案,该解决方案实际上并不会改变Shebang。
此解决方案使用系统python解释器来查找所需的解释器并执行该解释器。这对我很有用,因为它使我可以调整环境变量并确保正确的解释器。
由于它使用exec,因此不会使内存使用量增加一倍-新的解释器将替换最后一个解释器。另外,退出状态和信号将得到正确处理。
这是一个Python模块,由需要在此环境下运行的脚本加载。如果需要,导入此模块具有启动新解释器的副作用。通常,模块不应该有副作用,但是替代方法是在执行非系统导入之前运行模块的功能,这将违反PEP8。所以你必须选择你的毒药。
"""Ensure that the desired python environment is running."""
import os
import sys
def ensure_interpreter():
"""Ensure we are running under the correct interpreter and environ."""
abs_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.normpath(os.path.join(abs_dir, '../../../'))
desired_interpreter = os.path.join(project_root, 'bin/python')
if os.path.abspath(sys.executable) == desired_interpreter:
return
env = dict(os.environ)
def prefix_paths(key, prefix_paths):
"""Add prefix paths, relative to the project root."""
new_paths = [os.path.join(project_root, p) for p in prefix_paths]
new_paths.extend(env.get(key, '').split(':'))
env[key] = ':'.join(new_paths)
prefix_paths('PYTHONPATH', ['dir1', 'dir2'])
prefix_paths('LD_LIBRARY_PATH', ['lib'])
prefix_paths('PYTHON_EGG_CACHE', ['var/.python-eggs'])
env['PROJECT_ROOT'] = project_root
os.execvpe(desired_interpreter, [desired_interpreter] + sys.argv, env)
ensure_interpreter()
如果不需要修改任何环境变量,则可以删除env = dict(os.environ)
和os.execvpe(desired_interpreter, [desired_interpreter] + sys.argv, env)
之间的所有内容。