【问题标题】:Can a bash script that returns a Python interpreter replace the shebang?返回 Python 解释器的 bash 脚本可以替换 shebang 吗?
【发布时间】:2019-02-27 21:52:35
【问题描述】:

我有一些 Python 脚本,现在由各种各样的人跨多个测试平台运行。这造成了两个问题:

  1. python3 路径在所有测试人员中都不相同,并且
  2. 并非所有运行脚本的人都在 $PATH 中设置了 Python 路径。

因为 (1) 我无法对 shebang 进行硬编码,因为它依赖于测试器,并且因为 (2) #!/usr/bin/env python3 如果放在 Python 文件的顶部,则不能保证工作。

我知道 python3 解释器将位于测试人员的少数几个位置之一。所以我想知道的是,是否有可能将 Python 文件顶部的 #!/usr/bin/env python3 替换为对查找 python 的 bash shell 脚本的调用位置然后为脚本“设置”它?如果这不可能,那么剩下的就是没有意义的。

我创建了一个 bash 脚本,它会查看可能的位置,直到找到解释器,但我不知道如何将它返回到 Python 文件的顶部。

例如,我创建了一个基本的python文件(shebang.py)

#!./pyshebang.sh

print("Hello World")

pyshebang.sh 做了两件事,它将找到的 python 路径附加到 PATH,并将该路径回显给解释器。如果我运行上面的 python 脚本,stdout 会从 bash 脚本中获取回显,而不是从 python 脚本中获取打印。

【问题讨论】:

  • 不是真的;但知道正确解释器的位置不是您脚本的工作(或者至少不是您的 工作,作为脚本的作者)。这就是 installer 的工作,python setup.py ... 用安装程序指定的路径替换任何包含单词 python#!python 是最小示例)的 shebang。
  • 值得注意的是,/usr/bin/env python3 不仅仅是回显 bash 的路径。如果您直接在命令行上键入/usr/bin/env python3,您会看到它实际上是启动 python(很可能是通过exec() 的某种形式)。所以大概你的脚本在调用时必须做同样的事情。
  • 使您的用例通用。我相信应该让用户了解他们的 python 路径在哪里。
  • 阅读您的 execve 手册页:您不能将脚本作为 shebang。
  • 我建议一种策略是告诉您的用户 PATH 中的 python3 是运行您的程序所必需的。

标签: python python-3.x bash


【解决方案1】:

是否可以将 Python 文件顶部的 #!/usr/bin/env python3 替换为调用 bash shell 脚本,该脚本查找 python 位置,然后为脚本“设置”它?

是的,当然。这实际上就是/usr/bin/env python3 所做的。该特定命令没有什么神奇之处。它和它的变体恰好是广泛有用的。

我创建了一个 bash 脚本,它会查看可能的位置,直到找到解释器,但我不知道如何将它返回到 Python 文件的顶部。

您对正在发生的事情有误解。 shebang 行不会导致替换到脚本中。相反,它会导致指定的行作为命令执行,原始脚本的路径和它的参数作为附加参数附加。

因此,您的pyshebang.sh 应该具有以下一般形式:

#!/bin/bash
# Note: the above shebang line is not a special case

# ... find Python ...

MY_PYTHON=/the/python/I/discovered

# Execute the discovered Python, passing it all the arguments this
# script received
exec "$MY_PYTHON" "$@"

【讨论】:

  • 太棒了。感谢您的详尽解释。我只是试了一下,它完全按照我的意图工作。再次感谢!
  • 如果调用脚本是作为 Flask 服务器网关的 CGI 文件,您知道为什么这种方法不起作用吗?也就是说,我在只运行 CGI(没有 FCGI,更不用说 WSGI)的旧 Apache 上运行 Flask 服务器。我的app.cgi shebang 行指向一个shell 脚本,该脚本遵循您选择Python 然后execing 该python 的方法。但是,Apache 会给出错误日志,例如:(8)Exec format error: exec of '.../my-flask-app/app.cgi' failed Premature end of script headers: app.cgi 重定向脚本在命令行 python 脚本上运行良好
  • 不,@joelion,我不知道为什么这种通用方法会失败。就所呈现的详细程度而言,它仅依赖于 Bash 的众所周知的、有据可查的特性和一个底层的类似 POSIX 的操作系统。我可以想到各种细节,如果实施不正确,可能会引发您所描述的行为,但这可能最好在单独的问题中解决。如果您摆出一张,请务必附上minimal reproducible example
【解决方案2】:

您可以使用如下方法让 shell 找到正确的解释器,然后使用该解释器重新运行当前脚本。

#!/bin/sh

''':'
cmd=$(./pyshebang)
exec "$cmd" "$0" "$@"
:' '''

print("Hello")

这是一个shell脚本。第一个命令是:,由一个空的单引号字符串和一个带引号的: 构成。 : 是无操作的。下一个命令将变量cmd 设置为./pyshebang 返回的路径,然后是用$cmd 指示的程序替换当前进程的命令,将当前脚本名称作为其第一个参数并采用任何其他参数传递给 shell 脚本。

当同一文件随后由 Python 解释器运行时,它会看到一个文档字符串 ('''...'''),其内容(先前执行的 shell 代码)被忽略。然后从正确的 Python 代码开始执行,在找到 exec 命令之前,shell 从未查看过该代码。

【讨论】:

  • 我认为将$0 作为参数传递给exec 是不对的。你确定吗?我很确定解释器接收目标脚本名称作为参数,而不是它自己的名称,所以 OP 想要的解释器垫片只会重复它自己的参数到发现的 Python 命令。
  • 我假设垫片只是返回类似/usr/bin/python;这就是execed,它期望的第一个参数是实际运行的脚本。
猜你喜欢
  • 1970-01-01
  • 2018-11-05
  • 2011-09-06
  • 1970-01-01
  • 1970-01-01
  • 2014-10-13
  • 1970-01-01
  • 2013-02-20
  • 2016-01-17
相关资源
最近更新 更多