57

我的~/Documents. 我几乎只在 python 中工作,所以这些基本上都是 python 项目。例如,每一个~/Documents/foo都有自己的 virtualenv,~/Documents/foo/venv(它们总是被称为 venv)。每当我在项目之间切换时(每天大约 10 次),我都会这样做

deactivate
cd ..
cd foo
source venv/bin/activate

我已经到了厌倦打字deactivatesource venv/bin/activate. 我正在寻找一种方法来cd ../foo为我处理 virtualenv 操作。

  • 我对VirtualEnvWrapper很熟悉,在我看来这有点笨拙。据我所知,它似乎将您所有的 virtualenvs 移动到其他地方,并且增加了比它删除的复杂性更多的复杂性。(欢迎不同意见!)

  • 我对shell脚本不太熟悉。如果您可以推荐一个低维护脚本来添加到我~/.zshrc的实现这一点,那就足够了,但是通过一些快速的谷歌搜索,我还没有找到这样的脚本。

  • 我是zsh/oh-my-zsh用户。oh-my-zsh似乎没有这个插件。这个问题的最佳答案是有人贡献了一个oh-my-zsh插件来做到这一点。(如果这里的答案乏善可陈,我可能会这样做。

4

16 回答 16

32

在 .bashrc 或 .zshrc 中添加以下内容

function cd() {
  builtin cd "$@"

  if [[ -z "$VIRTUAL_ENV" ]] ; then
    ## If env folder is found then activate the vitualenv
      if [[ -d ./.env ]] ; then
        source ./.env/bin/activate
      fi
  else
    ## check the current folder belong to earlier VIRTUAL_ENV folder
    # if yes then do nothing
    # else deactivate
      parentdir="$(dirname "$VIRTUAL_ENV")"
      if [[ "$PWD"/ != "$parentdir"/* ]] ; then
        deactivate
      fi
  fi
}

即使有人进入子文件夹,此代码也不会停用 virtualenv。灵感来自@agnul@Gilles的答案。

如果 virtualenv 是由 pipenv 制作的,那么请考虑这个 wiki 页面

此外,为了增加安全性,请考虑direnv

于 2018-06-13T06:42:10.107 回答
15

把这样的东西放在你的 .zshrc

function cd() {
  if [[ -d ./venv ]] ; then
    deactivate
  fi

  builtin cd $1

  if [[ -d ./venv ]] ; then
    . ./venv/bin/activate
  fi
}

编辑:如评论cd中所述 - 进入当前虚拟环境的子文件夹会停用它。cd一个想法可能是仅在进入新环境时停用当前环境,例如

function cd() {
  builtin cd $1

  if [[ -n "$VIRTUAL_ENV" && -d ./venv ]] ; then
    deactivate
    . ./venv/bin/activate
  fi
}

这仍然可以改进,也许将其变成“提示命令”或尝试对文件夹名称进行一些前缀匹配以检查路径上某处是否存在虚拟环境,但我的 shell-fu 不够好。

于 2017-07-20T14:14:28.297 回答
14

您可以使用direnv而不是编写自定义脚本。它不是 zsh 特定的解决方案(因为您可以尝试zsh-autoenv),但维护良好且易于与 zsh 一起使用。一旦你安装了它,你会想把它放在eval "$(direnv hook zsh)"你的.zshrc. 那时你可以这样做:

$ source ~/.zshrc
$ cd foo
$ echo "layout python" > .envrc
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
$ direnv allow
direnv: loading .envrc
direnv: export +VIRTUAL_ENV ~PATH

现在你应该在你的 virtualenv 中。您可以通过运行pip freeze来测试以查看是否安装了您的 virtualenv 特定包。停用

$ cd ..
direnv: unloading
于 2018-09-10T17:25:28.097 回答
7

到目前为止,简单的选择(在 2019 年+)是添加virtualenvwrapper到您的~/.zshrc plugins

例如:

plugins=(
  git pip python brew virtualenvwrapper
)
于 2020-02-13T20:42:07.620 回答
7

如果不是direnv ,您应该尝试类似autoenv 的东西。

第一个被认为是“轻量级”,而第二个是“简单,质量更高的软件”,分别听各自的作者,谈论对方的项目。因此,在我看来,它们似乎是相当不错的选择,可以尝试两者!

无论如何,两者都在zsh外壳上进行了测试。尤其autoenv是使用起来真的很简单,安装之后:

$ git clone git://github.com/inishchith/autoenv.git ~/.autoenv
$ echo 'source ~/.autoenv/activate.sh' >> ~/.bashrc

就“跟着白兔”试试吧

$ mkdir project
$ echo "echo 'whoa'" > project/.env
$ cd project
whoa

“如果一个目录包含一个.env文件,当你进入它会自动执行cd。启用时(设置AUTOENV_ENABLE_LEAVE为非空字符串),如果一个目录包含一个.env.leave文件,它会在你离开时自动执行。”

查看https://github.com/inishchith/autoenv以获得更详细的说明!...

于 2020-05-18T00:17:54.787 回答
4

为了后代:我使用了@MS_的解决方案,但遇到了cd直接从一个项目到另一个项目的问题,它会停用旧的virtualenv,但不会激活新的virtualenv。这是解决此问题的解决方案的略微修改版本:

# auto activate virtualenv
# Modified solution based on https://stackoverflow.com/questions/45216663/how-to-automatically-activate-virtualenvs-when-cding-into-a-directory/56309561#56309561
function cd() {
  builtin cd "$@"

  ## Default path to virtualenv in your projects
  DEFAULT_ENV_PATH="./env"

  ## If env folder is found then activate the vitualenv
  function activate_venv() {
    if [[ -f "${DEFAULT_ENV_PATH}/bin/activate" ]] ; then 
      source "${DEFAULT_ENV_PATH}/bin/activate"
      echo "Activating ${VIRTUAL_ENV}"
    fi
  }

  if [[ -z "$VIRTUAL_ENV" ]] ; then
    activate_venv
  else
    ## check the current folder belong to earlier VIRTUAL_ENV folder
    # if yes then do nothing
    # else deactivate then run a new env folder check
      parentdir="$(dirname ${VIRTUAL_ENV})"
      if [[ "$PWD"/ != "$parentdir"/* ]] ; then
        echo "Deactivating ${VIRTUAL_ENV}"
        deactivate
        activate_venv
      fi
  fi
}
于 2019-05-25T23:40:47.737 回答
3

这是没有cd'ing的解决方案,将 zsh 设置为setop auto_cdw 将能够在没有 cd 的情况下更改目录,只需键入目录名称并按 Enter。它是上述解决方案的基础:

    # auto activate virtualenv
# Modified solution based on https://stackoverflow.com/questions/45216663/how-to-automatically-activate-virtualenvs-when-cding-into-a-directory/56309561#56309561
function auto_active_env() {

  ## Default path to virtualenv in your projects
  DEFAULT_ENV_PATH="./env"

  ## If env folder is found then activate the vitualenv
  function activate_venv() {
    if [[ -f "${DEFAULT_ENV_PATH}/bin/activate" ]] ; then 
      source "${DEFAULT_ENV_PATH}/bin/activate"
      echo "Activating ${VIRTUAL_ENV}"
    fi
  }

  if [[ -z "$VIRTUAL_ENV" ]] ; then
    activate_venv
  else
    ## check the current folder belong to earlier VIRTUAL_ENV folder
    # if yes then do nothing
    # else deactivate then run a new env folder check
      parentdir="$(dirname ${VIRTUAL_ENV})"
      if [[ "$PWD"/ != "$parentdir"/* ]] ; then
        echo "Deactivating ${VIRTUAL_ENV}"
        deactivate
        activate_venv
      fi
  fi
}
chpwd_functions=(${chpwd_functions[@]} "auto_active_env")
于 2019-08-15T21:34:03.840 回答
3

对于使用(或考虑使用)pyenv的任何人,可以使用此处描述的pyenv-virtualenv插件非常方便地实现这一点。

基本上,您只需将一个.python-version文件添加到指定 virtualenv 名称的目录中。

于 2021-03-18T15:38:28.110 回答
1

这是一个仅限 zsh 的解决方案。

这是对daveruinseverything 答案的改进,这是对MS_答案的改进。

我们使用precmd钩子而不是覆盖cd

我们添加了另一个额外的功能。假设目录结构是

├── .venv
│   ├── bin
│   │   └── activate
├── subdir
│   ├── subdir1
│   │   ├── subdir2
│   │   │   └── test2.txt
│   │   └── test1.txt
│   └── test.txt
└── testing.py

如果您现在在 subdir2 中打开新终端,或者直接从其他地方 cd 到 subdir2,它将激活 venv。

解决方案是:

autoload -Uz add-zsh-hook
add-zsh-hook precmd automatically_activate_python_venv

function automatically_activate_python_env() {
  if [[ -z $VIRTUAL_ENV ]] ; then
    activate_venv
  else
    parentdir="$(dirname ${VIRTUAL_ENV})"
    if [[ "$PWD"/ != "$parentdir"/* ]] ; then
      deactivate
      activate_venv
    fi
  fi
}

function activate_venv() {  
  local d n
  d=$PWD
  
  until false 
  do 
  if [[ -f $d/.venv/bin/activate ]] ; then 
    source $d/.venv/bin/activate
    break
  fi
    d=${d%/*}
    # d="$(dirname "$d")"
    [[ $d = *\/* ]] || break
  done
}
于 2020-09-18T12:37:16.960 回答
1

对于像我这样的python开发人员,我使用这个插件来制作,在进入python项目时激活python虚拟环境,在进入另一个目录cd后它也会停用。cd

于 2021-02-02T15:16:09.417 回答
1

类似于Jake 的回答,但支持cd从一个 virtualenv 到另一个 virtualenv。在这种情况下,它会停用旧的 virtualenv,然后激活新的 virtualenv。

function cd() {
  builtin cd "$@"

  if [[ ! -z "$VIRTUAL_ENV" ]] ; then
    # If the current directory is not contained
    # within the venv parent directory -> deactivate the venv.
    cur_dir=$(pwd -P)
    venv_dir="$(dirname "$VIRTUAL_ENV")"
    if [[ "$cur_dir"/ != "$venv_dir"/* ]] ; then
      deactivate
    fi
  fi

  if [[ -z "$VIRTUAL_ENV" ]] ; then
    # If config file is found -> activate the vitual environment
    venv_cfg_filepath=$(find . -maxdepth 2 -type f -name 'pyvenv.cfg' 2> /dev/null)
    if [[ -z "$venv_cfg_filepath" ]]; then
      return # no config file found
    fi

    venv_filepath=$(cut -d '/' -f -2 <<< ${venv_cfg_filepath})
    if [[ -d "$venv_filepath" ]] ; then
      source "${venv_filepath}"/bin/activate
    fi
  fi
}
于 2021-11-20T10:52:53.060 回答
0

这是我的解决方案,其中:

  • 检查是否已经处于当前活动状态venv,在这种情况下什么也不做
  • 如果有一个venv文件夹,如果有一个,则停用活动的
  • venv无论是否有旧的,都可以激活新的。

在我的bash_aliases

function cd() {
    builtin cd "$@"

    if [ $(dirname "$VIRTUAL_ENV") == $(pwd) ] ; then
        # Already at the active virtual env
        return
    fi

    if [[ -d ./venv ]] ; then
        if type deactivate > /dev/null 2>&1 ; then
            printf "Deactivating virtualenv %s\n" "$VIRTUAL_ENV"
            deactivate
        fi

        source ./venv/bin/activate
        printf "Setting up   virtualenv %s\n" "$VIRTUAL_ENV"
    fi
}
于 2019-04-24T14:26:00.343 回答
0

这是(还)另一种自动激活虚拟环境的解决方案;它基于此处已发布的许多答案。

这适用于任何虚拟环境名称或目录(不仅仅是./env,./venv等)。还支持子目录,以及cd-ing 到虚拟环境(父)文件夹的符号链接。

此代码搜索pyvenv.cfg文件而不是特定的命名目录。如果在当前文件夹的子目录中找到一个,则会自动激活环境。一旦进入虚拟环境,该状态将一直保留,直到您移出父虚拟环境目录,此时环境被停用。

把它放在你的.bashrcor.bash_profile中。

function cd() {
  builtin cd "$@"

  if [[ -z "$VIRTUAL_ENV" ]] ; then
      # If config file is found -> activate the vitual environment
      venv_cfg_filepath=$(find . -maxdepth 2 -type f -name 'pyvenv.cfg' 2> /dev/null)
      if [[ -z "$venv_cfg_filepath" ]]; then
        return # no config file found
      fi

      venv_filepath=$(cut -d '/' -f -2 <<< ${venv_cfg_filepath})
      if [[ -d "$venv_filepath" ]] ; then
        source "${venv_filepath}"/bin/activate
      fi
  else
    # If the current directory is not contained 
    # within the venv parent directory -> deactivate the venv.
      cur_dir=$(pwd -P)
      venv_dir="$(dirname "$VIRTUAL_ENV")"
      if [[ "$cur_dir"/ != "$venv_dir"/* ]] ; then
        deactivate
      fi
  fi
}

我个人认为这是对这里许多解决方案的改进,因为它应该适用于任何虚拟环境

于 2019-10-10T23:34:17.593 回答
0

这是我的解决方案:

  1. 如果未设置 VIRTUAL_ENV,则:
    1. 检查我们是否在虚拟环境中
    2. 如果是,则激活它
  2. 否则(定义了 VIRTUAL_ENV),检查当前文件夹是否以 $VIRTUAL_ENV 开头(删除/venv部分)并验证是否存在 deactivate 命令
    1. 停用环境

这是脚本:

function cd() {
  builtin cd $1

  if [[ -z "${VIRTUAL_ENV}" ]]; then
    if [[ -d ./venv && -f ./venv/bin/activate ]]; then
      source ./venv/bin/activate
    fi
  elif [[ ! "$(pwd)" == ${VIRTUAL_ENV:0:n-5}* && ! -z "$(command -v deactivate)" ]]; then
    deactivate
  fi
}

注意:您需要将此添加到.bashrc. 如果它不起作用,请检查您.profile是否没有覆盖您的命令(它发生在我身上)

于 2019-12-21T14:36:59.423 回答
0

基于@MS_的解决方案:

function cd() {
  builtin cd "$@"

  ## If env folder is found then activate the vitualenv
  if [[ -d ./venv ]] ; then
    source ./venv/bin/activate
  fi

  if [[ -n "$VIRTUAL_ENV" ]] ; then
    ## check the current folder belong to earlier VIRTUAL_ENV folder
    # if yes then do nothing
    # else deactivate
      parentdir="$(dirname "$VIRTUAL_ENV")"
      if [[ "$PWD"/ != "$parentdir"/* ]] ; then
        deactivate
      fi
  fi
}
于 2020-01-15T17:31:21.943 回答
0

正如其他人所提到的,我过去使用过direnv。Lyft针对这种情况使用激活器。

一旦构建了 venv,它必须被激活(添加到 $PATH)。我们使用 aactivator 来在用户每次进入服务目录时自动激活 venv,并在他们离开时停用。

于 2022-01-05T14:08:35.933 回答