15

我有一些 python-2.x 脚本,我在不同的系统、Debian 和 Arch linux 之间复制它们。Debian 将 python 安装为“/usr/bin/python”,而 Arch 将其安装为“/usr/bin/python2”。一个问题是在 Arch linux 上也存在引用 python-3.x 的“/usr/bin/python”。所以每次我复制一个文件时,我都必须更正 shebang 行,这有点烦人。

在 Arch 我使用

#!/usr/bin/env python2

在Debian上我有

#!/usr/bin/env python

由于 Debian 上不存在“python2”,有没有办法通过首选应用程序?也许有一些外壳扩展?例如,我不介意它是否取决于现有的 '/bin/sh'。以下会很好,但不起作用。

#!/usr/bin/env python2 python
#!/usr/bin/env python{2,}
#!/bin/sh python{2,}
#!/bin/sh -c python{2,}

令人沮丧的是'sh -c python{2,}' 在命令行上工作:即它在可用的地方调用python2,否则调用python。

我不希望在 Debian 上创建链接“python2->python”,因为如果我将脚本交给其他人,它就不会运行。我也不想让'python'指向 Arch 上的 python2,因为它会因更新而中断。

有没有一种干净的方法可以在不编写包装器的情况下做到这一点?

我意识到之前有人问过类似的问题,但我没有看到任何符合我的边界条件的答案:) 不同版本 Python 的条件 shebang 行

- - 更新

我拼凑了一个丑陋的外壳解决方案,现在可以完成这项工作。

#!/bin/bash
pfound=false; v0=2; v1=6
for p in /{usr/,}bin/python*; do  
  v=($(python -V 2>&1 | cut -c 7- | sed 's/\./ /g'))
  if [[ ${v[0]} -eq $v0 && ${v[1]} -eq $v1 ]]; then pfound=true; break; fi
done
if ! $pfound; then echo "no suitable python version (2.6.x) found."; exit 1; fi
$p - $* <<EOF

PYTHON SCRIPT GOES HERE

EOF

解释:获取版本号(v 是一个 bash 数组)并检查

v=($(python -V 2>&1 | cut -c 7- | sed 's/\./ /g'))
if [[ ${v[0]} -eq $v0 && ${v[1]} -eq $v1 ]]; then pfound=true; break; fi

使用标准输入 (-) 的输入启动找到的程序 $p 并传递参数 ($*)

$p - $* <<EOF
...
EOF
4

4 回答 4

22
#!/bin/sh
''''which python2 >/dev/null 2>&1 && exec python2 "$0" "$@" # '''
''''which python  >/dev/null 2>&1 && exec python  "$0" "$@" # '''
''''exec echo "Error: I can't find python anywhere"         # '''

import sys
print sys.argv

这首先作为 shell 脚本运行。您可以将几乎任何 shell 代码放在''''和之间# '''。此类代码将由 shell 执行。然后,当 python 在文件上运行时,python 将忽略这些行,因为它们看起来像 python 的三引号字符串。

shell 脚本测试二进制文件是否存在于路径中,如果存在which python2 >/dev/null则执行它(所有参数都在正确的位置)。有关这方面的更多信息,请参阅为什么这个带有 shebang #!/bin/sh 和 exec python 的片段在 4 个单引号内有效?

注意:该行以 4 开头,并且它们在第四个和 shell 命令的开头'之间不能有空格( ...)'which

于 2014-09-26T09:38:16.367 回答
1

像这样的东西:

#!/usr/bin/env python
import sys
import os
if sys.version_info >= (3, 0):
    os.execvp("python2.7", ["python2.7", __file__])
    os.execvp("python2.6", ["python2.6", __file__])
    os.execvp("python2", ["python2", __file__])
    print ("No sutable version of Python found")
    exit(2)

更新下面是一个更强大的版本。

#!/bin/bash

ok=bad
for pyth in python python2.7 python2.6 python2; do
  pypath=$(type -P $pyth)
  if [[ -x $pypath ]] ; then
    ok=$(
      $pyth <<@@

import sys 
if sys.version_info < (3, 0):
  print ("ok")
else:
  print("bad")
@@

    )
    if [[ $ok == ok ]] ; then
      break
    fi
  fi
done

if [[ $ok != ok ]]; then
  echo "Could not find suitable python version"
  exit 2
fi

$pyth <<@@
<<< your python script goes here >>>
@@
于 2013-09-25T00:15:57.667 回答
0

这是获得最高投票答案的更简洁的方法:

#!/bin/sh
''''exec $(which python3 || which python2 || echo python) $0 $@ #'''

import sys
print(sys.argv)
print(sys.version_info)

如果没有找到,你会得到这个:

'./test.py: 2: exec: python: not found'

此外,摆脱来自 linter 的警告:

'module level import not at top of file - flake8(E402)'
于 2020-06-06T11:34:37.387 回答
0

我将把它留在这里以供将来参考。

我自己的所有脚本通常都是为 Python 3 编写的,所以我使用 Aaron McDaid 答案的修改版本来检查 Python 3 而不是 2:

#!/usr/bin/env sh
''''which python3 >/dev/null 2>&1 && exec python3 "$0" "$@" # '''
''''test $(python --version 2>&1 | cut -c 8) -eq 3 && exec python "$0" "$@" # '''
''''exec echo "Python 3 not found." # '''

import sys
print sys.argv
于 2016-03-02T10:26:50.250 回答