I have discovered that there are zsh users
who torture themselves with unnecessary commands like find and
xargs. While I do not think this is something of which to be
ashamed, other solutions exist.
zsh has powerful globbing, which makes find irrelevant. For example, if one wanted to get a list of all files under /home that start with the character « q », one could do
find /home -name "q\*" -print
With zsh, one could type
print -l /home/\*\*/q\*
Another trivial example is that
find /home -name "q\*" -exec rm {} \\;
could become
for i (/home/\*\*/q\*) rm $i
I imagine that there are a number of cynics out there saying,
« Oh, no, asterisks confuse me, and so it is not worth it for
me to change my silly ways, and furthermore, find can do lots of
things. » Yes, find can do many things beyond filename
comparisons, but zsh is actually more powerful. Want to glob based
on file ownership? This find command
find /home -name "q\*" \\! -user clint -print
is so much more pleasant as
print -l /home/\*\*/q\*(\^u:clint:)
The reader is invited to figure out how to accomplish the task of identifying all files owned by the process's effective user ID with zsh, and compare it to the same task with an inferior shell.
Now, to introduce zargs, try a silly xargs example
echo this is a test of the emergency broadcast system | xargs -n2
The Z-Shell version is not much of an improvement
zargs -n2 this is a test of the emergency broadcast system
One may run into length issues when not using shell builtins. For example, this command would work regardless of how many files the glob matches, and regardless of what characters those filenames contain (whitespace will be handled fine, and the command will print one file per line, unless there are newlines within the filenames):
print -l /\*\*/j\*
This command will not work with a very long list of files, unless
one is explicitly using zsh's builtin rm command, which is not
loaded by default.
rm /\*\*/j\*
To successfully delete all those files beginning with « j » and owned by “mrwiggles”, one can do
for i in /\*\*/j\*(u:mrwiggles:); do rm $i ; done
which will handle any number of arguments, but will fork rm for
each file, or one can use
find / -name j\* -user mrwiggles -print0 | xargs -0 rm
(where it is necessary to delimit with NULs to avoid word-splitting problems introduced by whitespace) or, better yet,
zargs -- /\*\*/j\*(u:mrwiggles:) -- rm
Note that for each shell instance, you need to either load the
zargs function explicitly, or autoload the zargs function
explicitly, as in autoload -U zargs
These examples are fairly simplistic, but with more complex tasks, doing things the zsh way can pay off considerably.
Posted Mon 13 Jun 2005 03:36:37 PM EDT