direnv
2014年的苹果WWDC上有个口号:
Write the Code. Change the World.
然而我觉得 direnv 的作者则是:
Write the Code. Save the World.
这几年杂七杂八的东西学的太多了,几乎每一门语言都有自身版本和三方库版本的问题,比如java里面有maven这样的构建工具,来控制三方库的版本。更有甚者,如ruby的rvm,node的nvm,Python的virtualenv,go的gvm,groovy的GVM,haskell的stack,haskell的cabal。这些工具基本都肩负着三个使命,一是控制语言的版本,二是控制三方库的set,三是设置环境变量。于是每次到某个工程下面,就要执行 rvm use xxx
, nvm use xxx
,这些命令来切换环境。然后偶尔一次忘记,就会各种神奇的错误。
然而这几天,正好在测试同一个项目,用不同版本的语言编译的效果,结果在不同的环境切换下,竟然切晕了。虽然用 tmux 的分屏,多窗口等方法,勉强能凑活,但是还是偶尔几次忘记自己当前的环境。所以我想有没有能够,在不同目录下自动切换环境变量的工具。于是用google搜索 directory environment ,还真被我找到了。
安装
git clone https://github.com/direnv/direnv cd direnv make install
如果是以下系统的话,还可以用这些系统的包管理工具来安装,着实方便啊。
There's package definitions on Homebrew, Arch's AUR, Gentoo go-overlay overlay and NixOS's nixpkgs.
设置
ZSH
在 ~/.zshrc
中添加:
eval "$(direnv hook zsh)"
BASH
在 ~/.bashrc
中添加:
eval "$(direnv hook bash)"
FISH
在 ~/.config/fish/config.fish
中添加:
eval (direnv hook fish)
TCSH
在 ~/.cshrc
中添加:
eval `direnv hook tcsh`
使用
说到用法的话,差不多只要拿出首页的例子就可以了。
$ cd ~/my_project $ echo ${FOO-nope} nope $ echo export FOO=foo > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +FOO $ echo ${FOO-nope} foo $ cd .. direnv: unloading direnv export: ~PATH $ echo ${FOO-nope} nope
在想要自动切换环境的目录下面新建一个 .envrc
文件,文件的内容就是设置环境的命令。但是要主要的是 .envrc
只支持 bash 语法,因为这个文件最终也是在bash中执行,然后再设置到对应的shell中的。另外值得一提的是 ~/.direnvrc
中可以写入全局配置,比如加一些函数进去。direnv还提供了一个 stdlib ,通过 direnv stdlib
可以查看。
实战
比如我现在有个工程要用Python环境,于是只要在工程的根目录下面新建 .envrc
文件并
写入如下内容:
layout python
然后在首次进入这个目录的时候,就会在 .direnv
这个目录下建立,整套Python的virtualenv。然而PyCharm似乎不是很满意。PyCharm能够自动识别工程根目录下的 venv
目录。但是现在用direnv之后virtualenv的目录改变了。解决方案有很多。比如不用direnv的 layout python
,用virtualenv生成的active脚本,在 .envrc
中添加内容:
source venv/bin/active
或者修改 direnv stdlib 中的 python_layout
方法:
1: layout_python() { 2: local python=${1:-python} 3: local old_env=$PWD/virtualenv 4: unset PYTHONHOME 5: if [[ -d $old_env && $python = python ]]; then 6: export VIRTUAL_ENV=$old_env 7: else 8: local python_version 9: python_version=$("$python" -c "import platform as p;print(p.python_version())") 10: if [[ -z $python_version ]]; then 11: log_error "Could not find python's version" 12: return 1 13: fi 14: 15: export VIRTUAL_ENV=$PWD/venv 16: if [[ ! -d $VIRTUAL_ENV ]]; then 17: virtualenv "--python=$python" "$VIRTUAL_ENV" 18: fi 19: fi 20: PATH_add "$VIRTUAL_ENV/bin" 21: }