incava.org

Z-shell

This is an expanded version of a message I wrote about Zsh ("z shell") a couple of years ago, after switching to it from a background in tcsh and bash.

Completions:

In zsh, you have vast control over completions. For example, if I type

% cvs a<TAB>

I get the following possible completions:

add  admin  annotate

And if I tab again, I start going through the list. By completing on "add", I can do:

% cvs add <TAB>

This results in it prompting me only with the files that are not in CVS. Cool, eh? It does this by checking CVS/Entries, and filtering based on those files.

The following completion works for man pages, so that "man perl<TAB>" lists every Perl man page--all 129 of them, only about 3 of which I can remember. This is from my zsh configuration file:

manpath=( $(man -w | sed -e 's/:/ /g') )

# Manual page completion
man_glob () {
  local a
  read -cA a
  if  [[ $a[2] = [0-9n]* ]] then
    # specific section in manual ('n' is Tcl)

    # strip off .gz (if any), then the section number.

    reply=( $^manpath/man$a[2]/$1*(N:t:s/.gz//:r) )
  else
    # any section in manual

    reply=( $^manpath/man*/$1*(N:t:s/.gz//:r) )
  fi
}
compctl -f -K man_glob man

(Note that a similar function, from which this was adapted, uses the pattern $1*(N:t:r), but that strips only the first suffix, so /usr/share/man/man1/perlstyle.1.gz gets incorrectly converted to perlstyle.1 instead of perlstyle.)

Directory aliases:

You can set "usernames" to directories, using the tilde syntax. Since my directories tend to be very deeply nested, especially with Java packages, this gives me a nice short summary of where I am. For example:

~drj% cd ~mcw
~mcw% pwd
/opt/projects/code/subdirectory/anothersubdirectory/foo/bar

It "remembers" this, even when I'm moving around. So doing "cd .." and "cd bar" doesn't mess it up. And it can write the short name into the xterm title bar.

Alternative to "find":

Listing files recursively is far easier than doing, for example find . -name "*.cpp" -mtime -2. For example, to list .cpp files changed within the past two days:

~drj% ls **/*.cpp(m-2)
src/Main.cpp  syntj/src/SyntaxAnalyzer.cpp

To list all the directories (/) that were not (+) modified (m) within the past hour (1), excluding (~) any "doc" directories (*doc*):

~drj% ls -1d **/*~*doc*(mh+1/)
astj/src/
src/
statj/src/
syntj/src/

Notice that files are always in alphabetical order, another useful feature.

Prompt:

The "right prompt" can also be set, via the RPROMPT variable. This could be useful for reminders.

jpace@rigel ~drj %          [... edit area ...]          replace CDR

Process subtitution:

This feature automatically creates temporary files for retaining output of a process, useful for when you need multiple inputs into a second program. For example, to compare the lists of files of two hierarchies, thus showing the .cpp files that have been added and removed:

% comm -3 =(cd modifile.0; ls -1 **/*(.)) =(cd modifile; ls -1 **/*(.))
        buildrpms
        CVS/Entries
        CVS/Repository
        CVS/Root
        CVS/Template
[ ... other files ... ]

The subcommand =(cd modifile.0; ls -1 **/*(.)) subcommand changes directories, lists the files (.) in the older version of the project, and captures that output. (In reality, it's written to a temporary file, the name of which is passed to the comm command.)

So this is the equivalent of:

% cd modifile.0; ls -1 **/*(.) > /tmp/oldversion
% cd ..
% cd modifile; ls -1 **/*(.) > /tmp/newversion
% cd ..
% comm -3 /tmp/oldversion /tmp/newversion
% rm /tmp/oldversion /tmp/newversion

but requires far less typing.

Here is my Z-shell environment.

Here's another fun one, to determine which source files (under org) don't have unit tests. (My scheme is that for a class defined, e.g. as org/incava/util/Collect.java, its unit test will be in test/org/incava/util/TestCollect.java, that is, same package but under the test directory, and with "Test" prepended to the class name.)

% diff =(ls -1 org/**/*.java) =(ls -1 test/org/**/*.java | \
    perl -pe 's,test/,,; s,/Test,/,')

Or, equivalently:

% diff =(ls -1 org/**/*.java) =(cd test; ls -1 org/**/*.java | \ 
    perl -pe 's,/Test,/,')

Valid HTML 4.01!

Valid CSS!