我试着构建在任何地方都能运行的脚本。为此,我使用了一个定制的python,它始终位于相对于脚本的父目录中。
这样,我就可以将我的软件包加载到U盘上,无论U盘安装在哪里,也不管是否安装了python,它都可以在任何地方工作。
但是,当我使用
#!../python然后,只有当脚本从其目录中被调用时,它才能工作,这当然是不可接受的。
有没有办法做到这一点,或者在目前的shebang机制下这是不可能的?
发布于 2015-10-20 08:04:22
在this page上有一组适用于多种语言的健康的多行脚本,例如:
#!/bin/sh
"exec" "`dirname $0`/python" "$0" "$@"
print copyright如果你想要一行shebang,this answer (和问题)在细节中解释了这个问题,并建议在shebang中使用其他脚本的以下方法:
使用AWK
#!/usr/bin/awk BEGIN{a=ARGV[1];sub(/[a-z_.]+$/,"python",a);system(a"\t"ARGV[1])}使用Perl
#!/usr/bin/perl -e$_=$ARGV[0];exec(s/\w+$/python/r,$_)从11Jan21更新:
使用更新的env实用程序:
$ env --version | grep env
env (GNU coreutils) 8.30
$ env --help
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
Set each NAME to VALUE in the environment and run COMMAND.
Mandatory arguments to long options are mandatory for short options too.
-i, --ignore-environment start with an empty environment
-0, --null end each output line with NUL, not newline
-u, --unset=NAME remove variable from the environment
-C, --chdir=DIR change working directory to DIR
-S, --split-string=S process and split S into separate arguments;
used to pass multiple arguments on shebang lines因此,现在将-S传递给env就可以了。
发布于 2019-08-20 13:35:55
扩展@Anton的答案,这样路径中的空格和其他特殊字符就不会失败,并解释更多关于魔术的内容。
#!/bin/sh
"true" '''\'
exec "$(dirname "$(readlink -f "$0")")"/venv/bin/python "$0" "$@"
'''
__doc__ = """You will need to deliberately set your docstrings though"""
print("This script is interpretable by python and sh!")sh和python都能理解这个巧妙的脚本。每一个都会有不同的反应。shebang之后的前2行由sh解释,并使其将exec传递给一个相对的python二进制文件(提供了相同的命令行参数)。这些相同的行被python安全地丢弃,因为它们相当于一个字符串("true"),后跟一个多行字符串(‘)。
有关这个主题的更多信息,请参阅here。
发布于 2019-04-13 00:45:24
看过这些答案后,我决定使用特定于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)之间的所有东西。
https://stackoverflow.com/questions/20095351
复制相似问题