Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

My current best bet is:

for i in $(find . -name *.jpg); do echo $i; done

Problem: does not handle spaces in filenames.

Note: I would also love a graphical way of doing this, such as the "tree" command.

share|improve this question

5 Answers 5

up vote 11 down vote accepted

The canonical way is to do

find . -name '*.jpg' -exec echo {} \;

(replace \; with + to pass more than one file to echo at a time)

or (GNU specific, though some BSDs now have it as well):

find . -name '*.jpg' -print0 | xargs -r0 echo


for i (**/*.jpg(D)) echo $i
share|improve this answer
If you are using xargs -0, you should match it with find -0 –  itsbruce Oct 9 '12 at 8:35
@MichaelKjörling, no quoting {} makes no difference. {} is expanded by find, not the shell. An improvement would be not to use echo which expands escape sequences like \b (at least the Unix conformant echos). –  Stéphane Chazelas Oct 9 '12 at 8:46
@sch Are you sure? The find manpage shows find . -type f -exec file '{}' \; in the EXAMPLES section. Maybe some shells will treat braces specially. –  QuasarDonkey Oct 9 '12 at 13:38
Quotes don't harm but make no difference. All of '{}', \{\}, {}, "{}", '{'} are expanded by the shell (whatever Bourne-like shell) to one argument to find that is the two characters "{" and "}". Replace "find" with "echo find", or printf '<%s>\n' find if you want to double-check. –  Stéphane Chazelas Oct 9 '12 at 20:40
@QuasarDonkey See unix.stackexchange.com/questions/8647/… –  Gilles Oct 9 '12 at 21:49

Better answers have already been given.

But note that spaces are not the only problem in the code you gave. tab, newline and wildcard characters are also a problem.


set -f
for i in $(find . -name '*.jpg'); do echo "$i"; done

Then only newline characters are a problem.

share|improve this answer

What you have mentioned is one of the basic problems that people face, when they try to read file names. Sometimes, people with limited knowledge and having a misconception of file and folder structure tend to forget that "In UNIX Everything is a file" . So they don't understand that they need to handle spaces as well, as the file name can consist of 2 or more words with spaces.

Solution : So one of the better known ways of doing this is to do a clean read .

We here will read all the files present and keep them in a variable, next time when doing the desired processing we just will have to keep that variable inside quotes which will preserve the file names with spaces. This is one of the basic ways of doing it, however, the other answers provided by the other people here work just as well.


 find /path/to/files/ -iname "*jpg" | \
  while read I; do
   cp -v --parent "$I" /backup/dir/

Here i am reading the files by giving the path to them and whatever i am reading i am keeping them inside a variable I, which i will quote at a later point while processing it to preserve the spaces so as to process it correctly.

Hope this helps you in some way.

share|improve this answer
"everything is a file" refers to something else. Your solution is GUN specific and doesn't cope with backslash or newline characters in filenames. "while read" loops in shells (IMO) is often an indication of bad shell scripting practice. –  Stéphane Chazelas Oct 9 '12 at 9:57
@sch : huh, and i used to think, it's ok. Thanks for pointing that out.I probably need to look at it more . –  The Dark Knight Oct 9 '12 at 11:24
sorry, I meant "GNU", not "GUN" above (it's the first time I realise those are anagrams BTW...) –  Stéphane Chazelas Oct 9 '12 at 11:26

Simply with find and bash :

find . -name '*.jpg' -exec bash -c '
    echo "treating file $1 in bash, from path : ${1%/*}" 
' -- {} \;

This way, we use $1 in bash, like in a basic script, that open nice perspectives to performs any advanced (or not) tasks.

A more efficient way (to avoid having to start a new bash for every file):

find . -name '*.jpg' -exec bash -c 'for i do
    echo "treating file $i in bash, from path : ${i%/*}" 
  done' -- {} +
share|improve this answer

In recent version of bash, you can use the globstar option:

shopt -s globstar
for file in **/*.jpg ; do
    echo "$file"

For simple actions, you can even skip the loop entirely:

echo **/*.jpg
share|improve this answer
While that works in zsh (where the feature originated from) and with ksh93 (with set -G), it shouldn't be used in bash as bash version is fundamentally broken (just like GNU grep -r) as it descends into symlinks to directories (equivalent to find -L or zsh's ***/*.jpg). Also note that contrary to find, it will ommit dotfiles and not descend into dotdirs (by default). –  Stéphane Chazelas Oct 9 '12 at 8:53

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.