I want to list all .jpg files in all subdirectories using ls
.
For the same directory this works fine:
ls *.jpg
However, when using the -R
for recursiveness:
ls -R *.jpg
I get:
zsh:no matches found: *.jpg
Why does this not work?
Note: I know it can be done using find
or grep
but I want to know why the above does not work.
The shell first expands any glob patterns, and then runs the command. So from ls
's perspective, ls *.jpg
is exactly the same as if you had typed ls one.jpg two.jpg
. The -R
flag to ls
only makes sense if you use it on a directory, which you're not doing here.
This is also why mv *.jpg *.png
doesn't work as expected on Unix systems, since mv
never sees those patterns but just the filenames it expanded to (it does on e.g. Windows, where the globbing is done by the program rather than the shell; there are advantages and disadvantages to both approaches).
*
matches all characters except a /
, so *.jpg
only expands to patterns in the current directory. **/
is similar, but also matches /
, so it expands to patterns in any directory. This is supported by both bash and zsh.
So ls **/*.jpg
will do what you want; you don't need to use find
or grep
. In zsh, especially you rarely need to use find
since globbing is so much more powerful than in the standard Bourne shell or bash.
In zsh you can also use setopt glob_star_short
and then **.jpg
will work as well, which is a shortcut for **/*.jpg
.