Because you aren't quoting the regular expression you want to pass to grep
, it is being captured by the shell and the shell tries to expand it before calling grep
. Now, the regular expression you have used happens to also be a valid shell glob pattern1 which means "list all files or directories in the current folder whose name is any two characters, followed by the string o.txt
". If your current directory contains any files or subdirectories matching that glob, then the glob will be expanded to the matching file names:
$ ls
abo.txt demo.txt
$ cat demo.txt
foo.txt
$ set -x
$ grep [a-z][a-z]o.txt demo.txt
+zsh:7> grep abo.txt demo.txt
The set -x
tells zsh
to show the actual commands it runs. As you can see, [a-z][a-z]o.txt
is expanded to the matching file name: abo.txt
, and that is what is passed to grep
.
Now, you didn't actually have any matching file names in your directory, which means that zsh
expanded the pattern to an empty string and didn't even launch grep
at all. That's why the error is zsh: no matches found: [a-z][a-z]o.txt
. That is zsh telling you that it failed to expand the glob to any matching files.
And, finally, this is what happens if you quote the pattern properly:
$ grep '[a-z][a-z]o.txt' demo.txt
+zsh:12> grep '[a-z][a-z]o.txt' demo.txt
foo.txt
This time, the quotes protected the pattern from the shell, which passed it as is to grep
and grep
in turn interpreted it as a regular expression meaning "any two lower case letters, then an o
, then any other character, and ending with txt
". This is an important detail since .
in a regular expression means "any character". So what you really wanted, was:
grep '[a-z][a-z]o\.txt' demo.txt
The \.
"escapes" the .
so that it only matches a literal dot and no longer means "any character". So, always quote your regular expressions and be careful with .
!
Finally, here's an alternative way of writing this using extended regular expressions:
$ grep -E '[a-z]{2}o\.txt' demo.txt
foo.txt
1See What is the definition of a regular expression? for some more detail on globs vs regular expressions.
setopt NO_NO_MATCH
in your.zshrc
. This is one of the most annoying defaults in zsh, for someone coming from bash. – Jan 23 '21 at 05:06