BASH下改变PWD的另一种思路

有很多时候希望能通过命令改变当前进程的工作目录。
例如我曾厌倦于终端下很麻烦的才能从很深的子目录中切换到项目的根目录,没有办法让我通过一条简单的命令就直接做到么?我那时的想法是写一个sh文件,然后丢到PATH里,然后执行。但是我发现这样是行不通的,这个shell脚本会作为子进程执行,我尝试过export变量,然而如果了解UNIX原理的话会知道子进程是没法改变父进程的环境变量的,认识到这个事实让当时的我很沮丧。

最近在BYR论坛上有人提问,需求和我当初差不多,我有拾起了这个问题。我需要的是在我当前的进程上下文执行的程序来完成工作目录的转变,那么alias行么?可惜的是bash的alias不能加参数。source行么?这样做倒是能让程序由当前进程执行,但是必须这样用source scriptname,很明显没人会对这个结果满意。我当时就回复,这个功能是不可能实现的,自以为是的写了很长一段,现在看来奇怪的是居然没人反驳我。

最近遇到需求要用BASH,于是打算把SHELL捡起来,打算从BASH的man开始看。五千多行,2天多已经看了一半了,然而我几天前却以为需要几个月。于是在man page中意外的找到了解决办法

方法就是用sh的function,function会比alias和builtin先执行,并且是在当前上下文执行,这样需求可以完美的实现。
我立即把自己当时的想法写出来,那时候是为了方便的回到rails项目的根目录,但是现在已经很久不动ruby了。
代码如下:

function cd() {
    if [[ ! $PWD =~ ^$HOME ]]
    then
        builtin cd $1
        #echo "changed into here:" $PWD
    elif [[ $# -ne 0 ]]
    then
        builtin cd $1
        #echo "switched into here:" $PWD
    else
        while [[ "$PWD" != "$HOME" ]]
        do
            [[ -e .project_root ]] && break
            builtin cd ..
        done
    fi
}


把这个函数放在.bashrc里就可以。项目的根目录手动建一个.project_root文件。这时当在项目的子目录中直接cd的话就会回到项目根目录而不是用户的HOME目录。

顺便把bash的启动文件理清了一下
.bash_profile 当bash作为login interactive shell读取
.bashrc 当bash作为nonlogin interactive shell读取
$BASH_ENV 当bash作为noninteractive shell读取(当然需要这个变量存在,并且的确是一个可读文件)
ssh登录时读.bashrc
当bash作为sh启动时有点不一样
.profile login interactive shell
$ENV nonlogin interactive shell
noninteractive shell不会读任何启动文件
ssh登录时不读任何文件

有的时候困难看似不可战胜,其实可能这时的你对困难了解的还不够多。