shebang:使用相对于脚本路径的解释器

时间:2013-11-20 11:58:16

标签: python shebang

我尝试构建可以随处运行的脚本。为此,我使用了一个自定义构建的python,它始终位于相对于脚本的父目录中。

通过这种方式,我可以将我的软件包加载到USB记忆棒上,无论硬盘安装在何处以及是否安装了python,它都可以在任何地方使用。

然而,当我使用

#!../python

然后它只在从其目录调用脚本时才有效,这当然是不可接受的。

有没有办法做到这一点,或者在目前的shebang机制中这是不可能的?

5 个答案:

答案 0 :(得分:15)

this page有许多健康的多行shebang脚本用于很多语言,例如:

#!/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,$_)

答案 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)之间的所有内容。