Using zsh, I get a "No match found" message when choosing a pattern that does not fit with rm and that even when redirecting the output.
# rm * > /dev/zero 2>&1
zsh: no matches found: *
How can I get rid of this message?
Using zsh, I get a "No match found" message when choosing a pattern that does not fit with rm and that even when redirecting the output.
# rm * > /dev/zero 2>&1
zsh: no matches found: *
How can I get rid of this message?
This behaviour is controlled by several of Zsh's globbing options. By default, if a command line contains a globbing expression which doesn't match anything, Zsh will print the error message you're seeing, and not run the command at all. You can disable this in three different ways:
setopt +o nomatch
will leave globbing expressions which don't match anything as-is, and you'll get an error message from rm (which you can disable using -f, although that's a bad idea since it will force removals in other situations where you might not want to);
setopt +o nullglob
will delete patterns which don’t match anything (so they will be effectively ignored);
setopt +o cshnullglob
will delete patterns which don’t match anything, and if all patterns in a command are removed, report an error.
The last two override nomatch. All these options can be unset with setopt -o ….
nullglob can be enabled for a single pattern using the N glob qualifier, e.g.:
rm -f -- *(N)
nomatch is less than ideal, that's what Bourne-like shells do. If the pattern doesn't match, it is passed as-is to rm (!) upon which rm will give an error, or worse could delete the wrong file for a pattern like *.[ch] for instance!
– Stéphane Chazelas
Sep 22 '17 at 12:59
*.sh(N), not *(N).sh. SO ref, manual ref
– ThatsJustCheesy
Nov 11 '22 at 14:49
What would you want it to do instead? Not run rm at all (1)? Run it with a literal * argument like in other Bourne-like shells (2)? Run it with no argument at all (3)?
files=(*(N)); (($#files)) && rm -- $files. Or (rm -- *) 2> /dev/null but that would also hide genuine errors by rm which would be silly. You could discard the zsh error but restore stderr for the rm command though with (rm -- * 2>&3 3>&-) 3>&2 2> /dev/nullemulate sh -c 'rm -- *' 2> /dev/null. Then like in sh which zsh now emulated for that single command line, the non-matching * is passed as-is to rm and rm complains as that * file doesn't exist. We suppress rm's stderr as you would do in sh to suppress that error message, but again, that's silly as it would hide genuine errors by rm as opposed to the error incurred by the misbehaviour of sh passing a literal * to rm. rm -f '*' would not complain about an un-existing * file though, so you could do emulate sh -c 'rm -f -- *'rm -- *(N). rm would complain though when not passed any argument, though again, not rm -f: rm -f -- *(N).Generally, rm -f is the command you want to use if you want all the files gone and only get an error if files could not be removed or IOW are still there after rm has returned. You also generally want to use -f in scripts to avoid the user being prompted under some situations.
Here, calling rm when the glob doesn't match is wrong. The sh1 behaviour is wrong. It's harmless for a pattern like *, but for one like *.[ch], passing *.[ch] as-is when it doesn't match could cause the *.[ch] file to be removed by mistake:
$ ls
*.[ch] foo.txt
$ zsh -c 'rm *.[ch]'
zsh:1: no matches found: *.[ch]
$ ls
*.[ch] foo.txt
$ sh -c 'rm *.[ch]'
$ ls
foo.txt
Failing with an error is the most sensible thing to do and is what zsh (and fish, csh, tcsh, bash -o failglob and the original Unix shell) does.
And if you want to take care yourself of that special case, zsh makes it easy with its (N) glob qualifier (for nullglob) like in case (1) above. fish (at least in recent version) makes it even easier as its globs are expanded in a nullglob fashion when in arguments to the set command (the one that assigns variables). So, the equivalent there, would be:
set files *
if count $files > /dev/null
rm -f -- $files
end
See Why is nullglob not default for more details.
1. Strictly speaking it's only sh since the Bourne shell (since Unix V7 in 1979); earlier versions of sh (which did call /etc/glob upon unquoted wildcards which is where the glob name comes from) did behave like csh or zsh -o cshnullglob, that is /etc/glob would abort the command if none of the globs had any match (and would suppress the non-matching globs if at least one of them had any match). The behaviour was broken by the Bourne shell.
rm and not from the shell. Something like "rm: cannot remove `*': No such file or directory".
– Emmanuel
Sep 29 '16 at 13:05
emulate sh -c 'rm -- *' to get the (buggy IMO) behaviour of the Bourne shell.
– Stéphane Chazelas
Sep 29 '16 at 13:19
rm errors. That's something you would do in other shells to suppress the error when there's no matching file and that causes the suppression of genuine rm errors as well. My answer outlines that and hopefully shows how zsh behaviour is preferable.
– Stéphane Chazelas
Sep 29 '16 at 14:57
(N) is a glob qualifier - how's it different from *?
– baggiponte
Sep 27 '22 at 12:47
info zsh qualifiers. See also Why is nullglob not default?
– Stéphane Chazelas
Sep 27 '22 at 12:50
(N) qualifies, i.e. changes, the preceding *, so the full pattern is *(N). The qualifier changes the way the pattern behaves, and in this case the qualifier makes the pattern disappear if there is no match.
– Kusalananda
Sep 27 '22 at 14:13
*(N) and OMG is zsh so cool! I used it to update a function I have here
– baggiponte
Sep 27 '22 at 15:13
a && b || c as replacement for if a; then b; else c; fi. That's not correct.
– Stéphane Chazelas
Sep 27 '22 at 15:40
a && b but I wanted to add a message in case I did not run the command. tank you very much once again!
– baggiponte
Sep 29 '22 at 08:33
"$(dirname $HOMEBREW_BUNDLE_FILE)" or its more correct "$(dirname -- $HOMEBREW_BUNDLE_FILE)" is just $HOMEBREW_BUNDLE_FILE:h in zsh (like in csh).
– Stéphane Chazelas
Sep 29 '22 at 09:07
*.foo, *.bar, *.err files around. The .err files are only emitted when some failure conditions occur. What I want to do is simply rm *.foo *.bar *.err, I don't care of some of those files do not exist. Now that I know, I guess I can simply rm *.foo(N) *.bar(N) *.err(N).
– John Schmitt
Oct 21 '22 at 06:21
Type this in interactive zsh:
setopt no_nomatch
rm * > /dev/null 2>&1
setopt nomatch # `nomatch` is default, see `man zshoptions`
For me, nullglob and cshnullglob do not work
setopt nomatch again after the removal? Can we not add setopt no_nomatch to .zshrc? please share drawbacks of doing so if there are any.
– Anjan Talatam
Apr 07 '23 at 11:25
I could not comment due to lack of reputation. As a complement to Stephen Kitt's answer, I have to use setopt instead of setopt +o for the option to work. I am using zsh 5.9 .
setopt nullglob
declare -a files=(${SOME_PATH}/*.log)
for f in "${files[@]}"; do
printf "Removing ${f}...\n"
rm -f "${f}" || printf "Failed to remove ${f}\n"
done
If no match is found, the files array is empty, and the loop will not be entered, which meets my need perfectly.
setopt extended_glob(or even plainrm * >/dev/null 2>&1) but really you should make a workaround to not needrm *, that's outright dangerous – grochmal Sep 17 '16 at 18:18/dev/nullrather thendev/zero. Also, your stderr redirect is missing an&; it should be2>&1. – Chris Davies Sep 17 '16 at 18:29zshduring evaluation of the command and not during the runtime of the command itself (due to the error the command is not even run). The output redirections here would only affect the output of the command and not the shell itself. – Adaephon Sep 27 '16 at 12:53