84

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?

4
  • 1
    Are you trying to delete everything in the current directory? What is the current directory?
    – cutrightjm
    Commented Sep 17, 2016 at 18:15
  • 2
    You could use setopt extended_glob (or even plain rm * >/dev/null 2>&1) but really you should make a workaround to not need rm *, that's outright dangerous
    – grochmal
    Commented Sep 17, 2016 at 18:18
  • 2
    You should really be sinking to /dev/null rather then dev/zero. Also, your stderr redirect is missing an &; it should be 2>&1. Commented Sep 17, 2016 at 18:29
  • 5
    The error message is generated by zsh during 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
    Commented Sep 27, 2016 at 12:53

4 Answers 4

115

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)
4
  • 3
    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! Commented Sep 22, 2017 at 12:59
  • What if I don't want to disable the message, but I would like to continue without error? Like is, there a way to make the nomatch behavior simply print a warning rather than erroring?
    – Raleigh L.
    Commented Sep 14, 2022 at 22:15
  • 1
    @RaleighL. I’m not aware of any way of doing that. Commented Oct 14, 2022 at 12:03
  • 5
    For those unfamiliar with zsh's glob qualifiers, they must come at the end of the glob pattern. e.g., *.sh(N), not *(N).sh. SO ref, manual ref Commented Nov 11, 2022 at 14:49
21

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)?

  1. 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/null
  2. emulate 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 -- *'
  3. 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.

17
  • 2
    I think he expects to get the error message from rm and not from the shell. Something like "rm: cannot remove `*': No such file or directory".
    – Emmanuel
    Commented Sep 29, 2016 at 13:05
  • 1
    @Emmanuel, then that's 2: emulate sh -c 'rm -- *' to get the (buggy IMO) behaviour of the Bourne shell. Commented Sep 29, 2016 at 13:19
  • 2
    @Stephane_Chazelas as it is written, 2 is the right answer to a question he didn't asked :).
    – Emmanuel
    Commented Sep 29, 2016 at 14:35
  • 2
    @Emmanuel, all 3 answer the question: how to get rid of the "No match" error. The OP's code suppresses the 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. Commented Sep 29, 2016 at 14:57
  • 2
    @baggiponte Note that (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
    Commented Sep 27, 2022 at 14:13
5

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

1
  • why do we need to 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. Commented Apr 7, 2023 at 11:25
1

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .