简体   繁体   English

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

[英]How can I temporary bypass python virtual environment from inside a bash script?

I do have a bash script that needs to install some Python packages on the system instead of an virtual environment which may or may not be activated when the script is executed. 我确实有一个bash脚本,需要在系统上安装一些Python软件包,而不是在执行脚本时可能会激活的虚拟环境上安装虚拟环境。

This script is called by people that may already have a python virtual environment activated and I do want to be sure that for few commands I do not use it. 人们可能已经激活了python虚拟环境,因此人们调用了此脚本,但我想确保对于某些命令我不会使用它。

I tried to use the deactivate command but it seems that is not available, even if bash detects the virtual environment (presence of the VIRTUAL_ENV variable). 我尝试使用deactivate命令,但即使bash检测到虚拟环境(存在VIRTUAL_ENV变量),该命令似乎也不可用。

As a side note, I don't want to permanently disable the virtual environment. 附带说明,我不想永久禁用虚拟环境。 I just want to run few commands outside it. 我只想在其外部运行一些命令。 How can I do this? 我怎样才能做到这一点?

If activating before starting the script 如果启动脚本之前激活

If you did the activate step in a parent shell, not in the shell instance running the script itself, then non-exported variables and functions are unavailable during its runtime. 如果您在父外壳程序中执行了activate步骤,而不是在运行脚本本身的外壳程序实例中执行了activate步骤,则未导出的变量和函数在其运行期间将不可用。

To be entirely clear about definitions: 要完全清楚定义:

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.

You have three options: 您有三种选择:

  • Export the deactivate function and its dependencies from the parent shell, before starting the script . 在启动脚本之前 ,从父shell导出deactivate函数及其依赖项。

    This is as given below, and looks something like: 如下所示,看起来像:

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

    You could optionally define an activation function that does this for you, like so: 您可以选择定义一个为您执行此操作的激活函数,如下所示:

     # 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 
  • Make some guesses, within the script , about what the prior Python environment looked like. 在脚本中 ,对以前的Python环境的外观进行一些猜测。

    This is less reliable, for obvious reasons; 由于明显的原因,这不太可靠。 however, as virtualenv does not export the shell variables containing the original PYTHON_HOME , that information is simply unavailable to child-process shells; 但是,由于virtualenv不会导出包含原始PYTHON_HOME的shell变量,因此该信息对于子进程shell完全不可用; a guess is thus the best option available: 因此,猜测是可用的最佳选择:

     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 } 

    ...used within a limited scope as: ...在有限范围内用作:

     run_python_code_in_virtualenv_here (best_guess_deactivate; run_python_code_outside_virtualenv_here) run_python_code_in_virtualenv_here 
  • Run the script in a forked child of the shell that first sourced activate with no intervening exec() call 在第一源外壳的派生的子运行脚本activate中间没有exec()调用

    That is, instead of invoking your script as a regular subprocess, with: 也就是说,与其将您的脚本作为常规子进程调用,不如:

     # New shell instance, does not inherit non-exported (aka regular shell) variables ./my-shell-script 

    ...source it into a forked copy of the current shell, as ...将其作为当前shell的分叉副本,例如

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

    ...or, if you trust it to hand back control to your interactive shell after execution without messing up state too much (which I don't advise), simply: ...或者,如果您相信它可以在执行后将控制权交还给您的交互式外壳程序,而不会过多地破坏状态(我不建议这样做),只需:

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

    All of these approaches have some risk: Because they don't use an execve call, they don't honor any shebang line on the script, so if it's written specifically for ksh93, zsh, or another shell that differs from the one you're using interactively, they're likely to misbehave. 所有这些方法都有一定的风险:因为它们不使用execve调用,所以它们不遵守脚本上的任何shebang行,因此,如果它是专门为ksh93,zsh或不同于您的shell编写的,进行交互使用时,他们可能行为不端。

If activating within the script 如果脚本中激活

The most likely scenario is that the shell where you're running deactivate isn't a direct fork() ed child (with no intervening exec -family call) of the one where activate was sourced, and thus has inherited neither functions or (non-exported) shell variables created by that script. 最可能的情况是,你正在运行的外壳deactivate不是直接fork() ED孩子(中间没有exec -家庭电话)的一个地方的activate了采购,因而继承了既不功能或(非-exported)由该脚本创建的shell变量。

One means to avoid this is to export the deactivate function in the shell that sources the activate script, like so: 避免这种情况的一种方法是将deactivate函数导出到提供activate脚本的shell中,如下所示:

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'

My output from the above follows: 我从上面的输出如下:

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

Assuming you've fixed that, let's run once more through using a subshell to scope deactivation to make it temporary: 假设您已解决此问题,让我们再次使用子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

You could always reference the global Python directly: 您总是可以直接引用全局Python:

/usr/bin/python2.7 -E my_python_command

If you're concerned about such a path being unreliable, you could either: 如果您担心这种路径不可靠,则可以:

  • Configure Python in a unique, safe, static location upon installation, and reference that 安装后,在唯一,安全,静态的位置配置Python,并引用该文件
  • Invoke a subshell that is not inside the virtual environment 调用不在虚拟环境中的子shell
  • Use an alternate name for your virtualenv Python executable so that it doesn't conflict on the path. 为您的virtualenv Python可执行文件使用替代名称,以使其在路径上不会发生冲突。 ie. 即。 the virtualenv Python would be python-virtual, and python would still lead to the global installation. virtualenv Python将是python-virtual,而python仍将导致全局安装。

Then it would be something like: 然后将是这样的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM