My Hackergotchi

Updated: Never — Philip's Blog

Now featuring regular updates!

Sun, 04 May 2008

14:02 – Playing with zsh

Ever since ... well, forever, people have been telling me to try zsh. I have been a happy tcsh user since the beginning of time, and have never seen a reason to switch.

According to the documentation, zsh is a hybrid shell that combines all the useful features of tcsh and ksh, has somehow managed to find good features in bash to include as well (I wonder how that works, I've never managed to find any good features in bash, only annoyances) and adds some interesting new stuff on top.

The implied bloat of this alone has kept me away from zsh for a while. In fact, the binary is about the same size as bash. Which isn't bad, considering that bash takes all this space without offering any functionality and zsh offers heaps.

Being bored over the long weekend, I took a closer look at zsh and tried to teach it about the things I have grown to like in tcsh. This turned out to be surprisingly easy. In fact, the only mildly tricky bit was teaching zsh about the addictive run-fg-editor command, which I bind to ^Z and which is very addictive.

Six lines in my .zshrc do the trick, however:

# Emulate tcsh's run-fg-editor
run-fg-editor() {
    zle push-input
    BUFFER="fg %$EDITOR:t"
    zle accept-line
}
zle -N run-fg-editor

Another annoyance is the way the backward-delete-word function likes to eat whole paths. As far as I know, only tcsh gets this right by default. That is, if I've got something like cd /usr/local/tools/xchains/arm/arm9 in my buffer and press ^W, I only want the arm9 to go away, not the whole path. You'd think that would count as sensible default. Like the run-fg-editor bit, this was also fairly easy to fix:

# Emulate tcsh's backward-delete-word
tcsh-backward-delete-word () {
    local WORDCHARS="${WORDCHARS:s#/#}"
    zle backward-delete-word
}
zle -N tcsh-backward-delete-word

After I got zsh behaving like tcsh, I checked to make sure that some of the constructions I use all the time still worked:

[204] (philip@carrot)~/tmp% touch foo
[205] (philip@carrot)~/tmp% cp foo !#:1.orig
cp foo foo.orig
[206] (philip@carrot)~/tmp%

[206] (philip@carrot)~/tmp% !204:s/foo/bar/
touch bar
[207] (philip@carrot)~/tmp% !205:gs/foo/bar/
cp bar bar.orig
[208] (philip@carrot)~/tmp%

[208] (philip@carrot)~/tmp% !to
touch bar
[209] (philip@carrot)~/tmp% !cp
cp bar bar.orig
[210] (philip@carrot)~/tmp%

Yes - I'm a great fan of history substitution. And of the foreach loop and modifiers:

[210] (philip@carrot)~/tmp% mkdir foreach
[211] (philip@carrot)~/tmp% cd !$
cd foreach
[212] (philip@carrot)~/tmp/foreach% touch alpha beta gamma
[213] (philip@carrot)~/tmp/foreach% foreach file (*)
foreach> mv $file $file:u
foreach> end
[214] (philip@carrot)~/tmp/foreach% ll
total 0
-rw-rw-r--  1 philip  philip  0 May  4 13:49 ALPHA
-rw-rw-r--  1 philip  philip  0 May  4 13:49 BETA
-rw-rw-r--  1 philip  philip  0 May  4 13:49 GAMMA
[215] (philip@carrot)~/tmp/foreach%

I also make the occasional typo:

[215] (philip@carrot)~/tmp/foreach% iv GAMMA
zsh: correct 'iv' to 'vi' [nyae]?

Excellent. :-)

I'll be playing with this some more... I'm particularly interested in learning about the "fully programmable completion". If I read the documentation right, it's like tcsh completion on acid. Sounds like fun.

My only worry is that zsh is the "shell of choice" in the build system at work. So who knows what kind of horrible voodoo rites I'll need to perform to have both a sensible interactive environment (which the default isn't) and be able to create bootable builds (which a sensible interactive environment surely can't).

It seems zsh offers the possibility to be started with a different set of dotfiles. I think together with modules (thanks again for that tip, Jan, I think I already told you I'm addicted), I should be able to hack something together.

Fun fun fun!