我确实有一个bash脚本,需要在系统上安装一些Python软件包,而不是在执行脚本时可能激活或不激活的虚拟环境。
此脚本由可能已经激活了python虚拟环境的人调用,我确实希望确保对于少数命令我不使用它。
我尝试使用deactivate
命令但似乎不可用,即使bash检测到虚拟环境(存在VIRTUAL_ENV
变量)。
作为旁注,我不想永久禁用虚拟环境。我只想在它之外运行一些命令。我怎么能这样做?
答案 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 -E my_command # global
python-virtual my_command # virtual