如何从bash脚本中临时绕过python虚拟环境?

时间:2016-05-13 18:09:23

标签: python virtualenv

我确实有一个bash脚本,需要在系统上安装一些Python软件包,而不是在执行脚本时可能激活或不激活的虚拟环境。

此脚本由可能已经激活了python虚拟环境的人调用,我确实希望确保对于少数命令我不使用它。

我尝试使用deactivate命令但似乎不可用,即使bash检测到虚拟环境(存在VIRTUAL_ENV变量)。

作为旁注,我不想永久禁用虚拟环境。我只想在它之外运行一些命令。我怎么能这样做?

2 个答案:

答案 0 :(得分:3)

如果在启动脚本之前激活

如果您在父shell中执行activate步骤,而不是在运行脚本本身的shell实例中执行,则非导出的变量和函数在运行时期间不可用。

完全清楚定义:

source my-virtualenv/bin/activate # this runs in the parent shell

./my-shell-script # the shell script itself is run in a child process created by
                  # fork()+execve(); does not inherit shell variables / functions, so
                  # deactivate WILL NOT WORK here.

(source my-shell-script) # creates a subshell with fork(), then directly invokes
                         # my-shell-script inside that subshell; this DOES inherit shell
                         # variables / functions, and deactivate WILL WORK here.

您有三种选择:

  • 在启动脚本之前,从父shell 导出deactivate函数及其依赖项。

    如下所示,看起来像:

    source my-virtualenv/bin/activate
    export VIRTUAL_ENV ${!_OLD_VIRTUAL_@}
    export -f deactivate
    ./my-script-that-needs-to-be-able-to-deactivate
    

    您可以选择定义一个为您执行此操作的激活函数,如下所示:

    # put this in your .bashrc
    activate() {
      source "$1"/bin/activate && {
        export VIRTUAL_ENV ${!_OLD_VIRTUAL_@}
        export -f deactivate
      }
    }
    
    # ...and then activate virtualenvs like so:
    activate my-virtualenv
    
  • 在脚本中做一些关于先前Python环境的猜测。

    由于显而易见的原因,这种可靠性较低;但是,由于virtualenv不导出包含原始PYTHON_HOME的shell变量,因此该信息对于子进程shell是不可用的;因此,猜测是最佳选择:

    best_guess_deactivate() {
      if [[ $VIRTUAL_ENV && $PATH =~ (^|:)"$VIRTUAL_ENV/bin"($|:) ]]; then
        PATH=${PATH%":$VIRTUAL_ENV/bin"}
        PATH=${PATH#"$VIRTUAL_ENV/bin:"}
        PATH=${PATH//":$VIRTUAL_ENV/bin:"/}
        unset PYTHONHOME VIRTUAL_ENV
      fi
    }
    

    ...在有限的范围内使用:

    run_python_code_in_virtualenv_here
    (best_guess_deactivate; run_python_code_outside_virtualenv_here)
    run_python_code_in_virtualenv_here
    
  • 在首次采购activate的shell的分叉子项中运行脚本,而不进行exec()调用

    也就是说,不是使用:

    调用脚本作为常规子流程
    # New shell instance, does not inherit non-exported (aka regular shell) variables
    ./my-shell-script
    

    ...将其作为当前shell的分叉副本,将其作为

    # Forked copy of existing shell instance, does inherit variables
    (source ./my-shell-script)
    

    ...或者,如果你相信它可以在执行后将控制权交还给交互式shell而不会过多地搞乱状态(我不建议),只需:

    # Probably a bad idea
    source ./my-shell-script
    

    所有这些方法都存在一些风险:因为他们没有使用execve电话,他们不会尊重脚本上的任何shebang线,所以如果它是专门写的对于ksh93,zsh或其他与您以交互方式使用的shell不同的shell,他们可能会行为不端。

如果在脚本中激活

最有可能的情况是,您正在运行deactivate的shell不是直接fork() ed孩子(没有干预exec - 家庭电话) activate来源的那个,因此既没有继承该脚本创建的函数或(未导出的)shell变量。

避免这种情况的一种方法是在导出deactivate脚本的shell中导出activate函数,如下所示:

printf 'Pre-existing interpreter: '; type python

. venv-dir/bin/activate

printf 'Virtualenv interpreter: '; type python

# deactivate can be run in a subshell without issue, scoped to same
printf 'Deactivated-in-subshell interpreter: '
( deactivate && type python ) # this succeeds

# however, it CANNOT be run in a child shell not forked from the parent...
printf 'Deactivated-in-child-shell (w/o export): '
bash -c 'deactivate && type python' # this fails

# ...unless the function is exported with the variables it depends on!
export -f deactivate
export _OLD_VIRTUAL_PATH _OLD_VIRTUAL_PYTHONHOME _OLD_VIRTUAL_PS1 VIRTUAL_ENV

# ...after which it then succeeds in the child.
printf 'Deactivated-in-child-shell (w/ export): '
bash -c 'deactivate && type python'

我的输出如下:

Pre-existing interpreter: python is /usr/bin/python
Virtualenv interpreter: python is /Users/chaduffy/test.venv/bin/python
Deactivated-in-subshell interpreter: python is /usr/bin/python
Deactivated-in-child-shell (w/o export): bash: deactivate: command not found
Deactivated-in-child-shell (w/ export): python is /usr/bin/python

假设你已经解决了这个问题,那么让我们再次使用子shell进行范围去激活以使其成为临时的:

. venv-dir/activate

this-runs-in-venv

# minor performance optimization: exec the last item in the subshell to balance out
# ...the performance cost of creating that subshell in the first place.
(deactivate; exec this-runs-without-venv)

this-runs-in-venv

答案 1 :(得分:0)

您可以直接引用全局Python:

/usr/bin/python2.7 -E my_python_command

如果您担心这样的路径不可靠,您可以:

  • 在安装时将Python配置在一个独特,安全的静态位置,并引用
  • 调用不在虚拟环境中的子shell
  • 为virtualenv Python可执行文件使用备用名称,以使其在路径上不冲突。即。 virtualenv Python将是python-virtual,而python仍将导致全局安装。

那就是:

python -E my_command # global  
python-virtual my_command # virtual