Why does rm in zsh abort?

Solution 1:

With the zsh, the (very sane) default is to error on globs that can't be expanded to existing files. In other shells, the usual default is to leave the glob untouched (which may or may not be the right thing to do depending on the command - and it may very well be a very wrong thing to do). I suspect your Linux configuration has this overridden somewhere to follow the behaviour of other shells.

If you're using zsh on both shells, IMHO the best option would be to qualify globs that are allowed to expand to nothing using the (N) qualifier:

% echo .zshc*
zsh: no matches found: .zshc*
% echo .zshc*(N)

%

Of course, you can change this behaviour globally. Pick whichever of the following most suits your needs:

setopt nullglob # all globs can expand to nothing
setopt +o nomatch # leave globs as-is instead of reporting error

Solution 2:

By default, in zsh, if a wildcard pattern doesn't match anything, zsh signals an error and doesn't run the command. Note that the error comes from zsh, not from rm.

This is usually a useful feature, since in many cases, if you use a wildcard pattern, you intend for it to match something. However, in scripts (and more generally in automated processes such as your alias), it's common to process all the files that match a certain pattern, and if no files match, then you just do nothing. You can use the N glob qualifier for that. For example:

for x in *.dat(N); do
  rm -- $x
done

If you are writing a function or script where all the wildcard patterns fit this usage, you can turn on the null_glob option. This is essentially equivalent to putting (N) at the end of every wildcard pattern.

The best way to make your alias work in zsh is to turn it into a function and set the null_glob option locally. The reason it has to be a function is that you need to do something more complex than run a command with some predefined arguments: you also need to temporarily set an option. You should also pass the -f option to rm and not ignore errors from it. This way you'll see if something fails, for example because you don't have permission to delete the files. You'll see no output in normal cases, whether some files happen to get deleted or not.

clean_some_files () {
  setopt local_options null_glob
  rm -f broyd  broyd.7 cdn* *.dat *.npy stars wkf2  fleurinputschema.xsd judft_times juDFT_times.json  inf out  out.xml  usage.json struct.xsf juDFT_times FleurInputSchema.xsd mixing_history*
}

When you have multiple patterns, there's also the csh_null_glob option. The difference with null_glob is that if you have a series of patterns, zsh still signals an error if none of the patterns match. For example cat *.txt *.md works as long as there is at least either a .md file or .txt file, and signals an error if there is none (as opposed to the null_glob behavior which would run cat with no arguments). I recommend turning this option on in your .zshrc:

setopt csh_null_glob

There's also a nomatch option which is on by default (except when zsh is emulating sh or ksh). If you turn it off, zsh will leave non-matching patterns alone instead of raising an error. This is convenient when you're using wildcard characters in something that isn't meant to be a wildcard pattern, for example a URL containing ?. But it's also error-prone, so I recommend keeping nomatch on.