为什么带有尾随冒号的 PYTHONPATH 将当前目录添加到 sys.path?

FMc*_*FMc 5 python import pythonpath sys.path

考虑这样一个 Python 项目:

foo/
    __init__.py
scripts/
    run.py
demo.sh
Run Code Online (Sandbox Code Playgroud)

在正常情况下,foo如果您从项目的根目录运行脚本,则尝试从包导入将失败,因为默认的 Python 行为是将调用 Python 解释器的脚本目录 (不一定是当前目录)添加到sys.path. (文档):

python scripts/run.py
Run Code Online (Sandbox Code Playgroud)

然而,我最近注意到这样的导入在我的盒子上工作,我追踪到一些与 PYTHONPATH 相关的令人惊讶的行为。在我的 Bash 配置文件中,我向 PYTHONPATH 添加了一个目录:

export PYTHONPATH="/some/path:$PYTHONPATH"
Run Code Online (Sandbox Code Playgroud)

如果 PYTHONPATH 最初是空的,那么(有点草率,但常见)形式的命令将留下一个尾随冒号:

echo $PYTHONPATH
/some/path:
Run Code Online (Sandbox Code Playgroud)

我一直认为这个尾随标点符号没有影响,但尾随冒号似乎是神秘成功导入的原因。前导或尾随冒号(或甚至一个定义但空PYTHONPATH)将导致sys.path含有一个空字符串之前site 模块加载,这反过来又导致在当前工作目录被添加到sys.path

这里有一个 Python 脚本和一个 Bash 脚本来演示。我使用 Python 2.7 和 Python 3.3 得到了相同的行为。

Python脚本run.py::

import sys, os

pp = os.environ.get('PYTHONPATH')

try:
    import foo
    print 'OK'
    assert os.getcwd() in sys.path
    assert pp == '' or pp.startswith(':') or pp.endswith(':')

except Exception:
    print 'FAIL'
    assert os.getcwd() not in sys.path
    assert pp is None
Run Code Online (Sandbox Code Playgroud)

Bash 脚本: demo.sh:

# Import fails.
unset PYTHONPATH;  python scripts/run.py

# Import succeeds -- to my surprise.
PYTHONPATH=''      python scripts/run.py
PYTHONPATH='/tmp:' python scripts/run.py
PYTHONPATH=':/tmp' python scripts/run.py
Run Code Online (Sandbox Code Playgroud)

我的问题

  • 我是否正确理解了情况,还是不知何故误入歧途?

  • 这在任何地方都有记录吗?我没有找到任何东西。至少,我会在此处发布此信息,以防对其他人有所帮助。

  • 只有我一个人发现这种行为出乎意料吗?

Dav*_*vid 6

修改冒号分隔的环境变量(例如 PYTHONPATH、PATH、CPATH、MANPATH、LD_LIBRARY_PATH、PKG_CONFIG_PATH 等)时...其中一些变量对尾随冒号赋予特殊含义,而另一些则没有。

对于 PYTHONPATH 和 PATH,我建议在之前未设置变量的情况下以不会意外引入尾随(或前导)冒号的方式添加(或追加)新目录:

export PYTHONPATH="/some/path${PYTHONPATH+":"}${PYTHONPATH-}"
Run Code Online (Sandbox Code Playgroud)

(对于 MANPATH 和 INFOPATH,您确实需要引入一个尾随冒号,以便maninfo将包含它们的默认搜索目录。)

解释:

  • ${PYTHONPATH+":"}如果设置了 PYTHONPATH,则扩展为 a :,无论 PYTHONPATH 是否为空。
  • ${PYTHONPATH-}如果设置了 PYTHONPATH,则将扩展为 PYTHONPATH 的内容,但如果未设置 PYTHONPATH,则${PYTHONPATH-}扩展为空 --- 就像通常的${PYTHONPATH}.

    • ${PYTHONPATH-}${PYTHONPATH-""}与未设置 PYTHONPATH 时替换“”(无)的含义相同。
    • ${PYTHONPATH-}我在这里推荐的原因${PYTHONPATH}是,${PYTHONPATH-}当 PYTHONPATH 未设置并且脚本已执行set -u以在未设置的变量上引发错误时,不会产生错误。

有关${parameter+[word]}${parameter-[word]}机制的详细信息,请参阅“参数扩展”:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02

有关 的详细信息set -u,请参阅“set”的描述:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set