Tuesday, November 17, 2009

Nicer pushd, popd, and dirs

Writing up the safer, friendlier bash which command reminded me of another wrapper function that's handy to have. If you use pushd/popd to keep a stack of directories in your shell window, sometimes you want to switch to a directory far down the stack. If you want to switch to the third directory down, you do pushd +3.

The problem is, you have to count along the list of directories, since dirs, pushd, and popd all just spit out a one-line list of the stack, like this:

$ dirs
/usr/local/share ~ ~/work ~/tmp
Not an impossible task, but it can get annoying if you have some long paths in there, especially once your stack gets past two or three deep. Why not have dirs print one directory per line, and label them with their depths? To do so, add this function to your .bashrc file:

function dirs {
ds=(`command dirs`)
i=0
while [ "${ds[$i]}" != "" ]; do
echo $i: ${ds[$i]};
i=$((i+1));
done
}
Now you get more readable output:

$ dirs
0: /usr/local/share
1: ~
2: ~/work
3: ~/tmp
If you want to remove ~/work from the stack, just do popd +2.

Since pushd and popd also print the directory stack, let's add the same style of output to them:

function pushd {
if command pushd $@ > /dev/null; then
dirs
fi
}

function popd {
if command popd $@ > /dev/null; then
dirs
fi
}
One final note. In case some wacky systemwide file has aliased these commands to something else, add the following unaliasing code to the top of your .bashrc:

for func in dirs pushd popd; do
if alias $func > /dev/null 2>&1 ; then unalias $func; fi
done
Otherwise, your functions will be hidden (aliases take precedence).

PS: emacs shell-mode has a conflict with this dirs: here's how to fix it.

3 comments:

Rob S. said...

One of the things I don't like about the bash version of pushd/popd is the fact that you cannot turn off the auto printing of dirs with each push or pop (as far as I am aware). In zsh you can control this with a setopt PUSHD_SILENT in your .zshrc - this seems to keep popd quiet as well.

As to your improved dirs function, I am curious though - why not just use 'dirs -v' instead of rolling your own? One thing is for certain, if we must look at the dirs output every time we push or pop in bash, we might as well see it in a dirs -v format.

Rob S. said...

Doh - I mentioned in my previous comment that there was no way to silence the dirs output in bash... except to redirect it to /dev/null, such as you demonstrated in your post. Must be too early in the morning for me. :)

Bill Night said...

Oops, I have to admit that I didn't know about dirs -v. Thanks for pointing it out.

I do like having pushd/popd give the longer output, which could also be done with dirs -v.