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.

Monday, November 2, 2009

Keep from Killing Emacs Shell Buffers

If you're someone who lives in emacs like I do, then you probably rely on shell buffers (M-x shell) to interact with the operating system. That way you can search through output and edit history commands in a natural way that a mere terminal doesn't allow.

But occasionally I've been left kicking the wall after accidentally killing a shell buffer that had lots of work in it that I still needed. It really hurts when you accidentally kill a buffer where you're waiting for a long-running process to finish. To prevent industrial accidents like that, add a guard to kill-buffer-hook in your .emacs file:


(add-hook 'kill-buffer-hook
'(lambda()
(and
(string= "Shell" mode-name)
(or
(yes-or-no-p
(format "Kill buffer `%s'? " (buffer-name)))
(error "Aborted")))))


If you really do want to kill the shell buffer, type "yes", and you're done. But it will save you some grief if you just hit C-x k in the wrong buffer.

What's more common for me is that my eyes are on a file that I want to refresh or replace with C-x C-v (find-alternate-file), but the cursor is in a shell buffer. In that case, emacs will indeed load the alternate file, and you'll be asked "Kill buffer ` **lose**'?" Say no, then you'll have C-x b to " **lose**" (note the leading space), and rename the shell buffer to "*shell*" or whatever you had previously named it.