一个Shell函数如何知道它是否在虚拟环境中运行?

46

如何在bash函数中测试是否运行在Python虚拟环境中?

有两种方法:

[[ "$(type -t deactivate)" != function ]]; INVENV=$?

或者

[[ "x$(which python)" != "x$VIRTUAL_ENV/bin/python" ]]; INVENV=$?

(注:希望如果我们在虚拟环境内部时$INVENV为1,否则为0,这就是上面的后向测试所强制要求的。)有没有更少hacky的方法?

还有一个名为 command -v 的shell内置命令。 - phk
2020年可行的答案(Python>=3.6):https://dev59.com/H3I-5IYBdhLWcg3wZ3jC#42580137 - arielf
3个回答

66
if [[ "$VIRTUAL_ENV" != "" ]]
then
  INVENV=1
else
  INVENV=0
fi
// or shorter if you like:
[[ "$VIRTUAL_ENV" == "" ]]; INVENV=$?

编辑:正如@ThiefMaster在评论中提到的,在某些情况下(例如,在活动的虚拟环境中启动新的 shell 时,可能在tmuxscreen中),此检查可能会失败(但是,从虚拟环境内部启动新的 shell 也可能会导致其他问题,我不推荐这样做)。


不确定 $PATH 与此有何关系?或者您是想检查当前工作目录是否属于 virtualenv 的一部分? - robertklep
抱歉我的评论有些混乱;如果 $VIRTUAL_ENV 不在 $PATH 中,那么其他所有程序使用的 python 可执行文件将不是虚拟环境中的文件。仅仅给 $VIRTUAL_ENV 赋值并不能起到任何作用。 - kjo
我同意,但我的代码片段并没有给 $VIRTUAL_ENV 赋值,它只是检查它。当它存在时,这意味着一个虚拟环境是活动的(该变量由 activate 脚本设置并由 deactivate 取消设置)。这不就是你想要的吗? - robertklep
5
这不是可靠的。例如,在虚拟环境内启动 tmux 会话时,虚拟环境并没有激活,但 $VIRTUAL_ENV 仍然被设置了。 - ThiefMaster
在你的回答中提到它可能会在某些情况下失败是个好主意。 - ThiefMaster
显示剩余3条评论

18

其实我刚找到一个类似的问题,从那里很容易得出这个问题的答案:

Python: 判断是否在虚拟环境中

例如,一个 shell 脚本可以使用类似如下的命令:

python -c 'import sys; print (sys.real_prefix)' 2>/dev/null && INVENV=1 || INVENV=0

(感谢Christian Long展示了如何使这个解决方案在Python 3中也可以工作。)

编辑:这里有一个更直接的(因此更清晰、更简洁)解决方案(参考JuanPablo的评论):

INVENV=$(python -c 'import sys; print ("1" if hasattr(sys, "real_prefix") else "0")')

3
一个可选的命令用于检查:python -c 'import sys; print hasattr(sys, "real_prefix")' - JuanPablo
2
在Python 3中添加括号以支持 python -c 'import sys; print(sys.real_prefix)' 2>/dev/null && INVENV=1 || INVENV=0 - Christian Long
3
我的Python环境(python3 -m venv venv)没有sys.real_prefix属性。这个作为替代方案是否可行? INVENV=$( python -c 'import sys ; print( 0 if sys.prefix == sys.base_prefix else 1 )' ) - BrendanSimon
@BrendanSimon 在我看来很好。 - Broshi
1
直接在终端上运行时可以工作,但似乎无法从 bash 脚本内部工作(在 venv 中运行)! - quanta
1
你也可以将其转换为 int(),而不是使用 if 语句:INVENV=$(python3 -c 'import sys; print(int(hasattr(sys, "real_prefix")))')但是,当从简单的 bash 脚本运行时,我无法确认脚本是否正在使用 venv。下面的答案使用 $VIRTUAL_ENV 变量(由 activate 脚本设置)确定了这一点。 - Dostrelith

1
如果您使用virtualenvwrappers,那么运行的预/后脚本可以为您设置INVENV。
或者,您可以在您的.bashrc中添加以下内容,并在您的工作目录(用于Django)中创建一个名为.venv的文件,这样当您进入该目录时,虚拟环境将自动加载。
export PREVPWD=`pwd`
export PREVENV_PATH=

handle_virtualenv(){
    if [ "$PWD" != "$PREVPWD" ]; then
        PREVPWD="$PWD";
        if [ -n "$PREVENV_PATH" ]; then
            if [ "`echo "$PWD" | grep -c $PREVENV_PATH`" = "0"  ]; then
                deactivate
                unalias python 2> /dev/null
                PREVENV_PATH=
            fi
        fi

        # activate virtualenv dynamically
        if [ -e "$PWD/.venv" ] && [ "$PWD" != "$PREVENV_PATH" ]; then
            PREVENV_PATH="$PWD"
            workon `basename $PWD`
            if [ -e "manage.py" ]; then
                alias python='python manage.py shell_plus'
            fi
        fi
    fi
}

export PROMPT_COMMAND=handle_virtualenv

谢谢你的代码。顺便说一下,我认为bash已经维护了OLDPWD,这与你代码中的PREVPWD是相同的东西。 - kjo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接