Conda activate.d 和 deactivate.d 不符合我的预期

Conda activate.d and deactivate.d do not behave what I expect

我正在尝试使用 activate.d 和 deactivate.d 修改我在 conda 中的 $PATH 变量,例如:

在activate.d中:

export OLD_PATH = $PATH
export PATH= "/path/to/something:$PATH"

在德activate.d:

export PATH = $OLD_PATH
unset OLD_PATH

base环境中,echo $PATH输出:

(base) $ echo $PATH
/home/myname/anaconda3/bin:/home/myname/anaconda3/condabin:/home/myname/bin:/home/myname/.local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/cuda-8.0/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin

然后我通过以下方式激活我的环境:

(base) $ conda activate myproject

myproject环境下,echo $PATH输出:

(myproject) $ echo $PATH
/path/to/something:/home/myname/anaconda3/envs/myproject/bin:/home/myname/anaconda3/condabin:/home/myname/bin:/home/myname/.local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/cuda-8.0/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin

这正是我所期望的。但是,当我尝试使用

返回到 base 环境时
(myproject) $ conda deactivate

我期望的是 echo $PATH 将输出与在 base 环境中相同的输出。然而,我得到的是:

(base) $ echo $PATH
/home/myname/anaconda3/envs/myproject/bin:/home/myname/anaconda3/condabin:/home/myname/bin:/home/myname/.local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/cuda-8.0/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin

注意第一个路径是 /home/myname/anaconda3/envs/myproject/bin 而不是 /home/myname/anaconda3/bin

那里发生了什么事?

不对称破坏可逆性

问题是 Conda 也在操纵 PATH 变量并且脚本的顺序 运行 与正确的逆运算不一致。即activate和deactivate的操作顺序都是:

  1. 更新环境变量(包括PATH
  2. 运行 个脚本在 etc/conda/(de)activate.d 个文件夹中。

为了与逆运算一致,需要运算对称。也就是说,activate 应该做 (1) 然后 (2),deactivate 应该做 (2) 然后 (1)。

相反,您保存的 OLD_PATH 实际上对应于 PATH 被操纵以激活环境后

解决方法

您可以不尝试捕获 OLD_PATH,而是只跟踪在停用脚本中添加和删除的内容。例如,像

activate.d 脚本

export MY_ADDED_PATH="/path/to/something"
export PATH="$MY_ADDED_PATH:$PATH"

deactivate.d脚本

# Credit for escaping: https://unix.stackexchange.com/a/129063/148899
escaped_lhs=$(printf '%s\n' "$MY_ADDED_PATH:" | sed 's:[][\/.^$*]:\&:g')
export PATH=$(echo "$PATH" | sed "s/$escaped_lhs//") 
unset MY_ADDED_PATH

这似乎适用于 osx-64


调试提示

Conda 处理起来很麻烦,因为它混合了 shell 函数和 Python。然而,一件巧妙的事情是 Conda 入口点(例如,bin/conda)有一些内部函数,return 串 shell 命令。

具体来说,当 运行 执行命令 conda activate foo 时,可以检查 shell 中的 运行 是什么,使用

$CONDA_EXE shell.posix activate foo

同样,

$CONDA_EXE shell.posix deactivate

将列出停用操作。这并不是 Conda 在激活和停用方面所做的全部,但它涵盖了大部分内容。