How do I perform an action on all files with a specific extension in subfolders in an elegant way?

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.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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

zsh:

for i (**/*.jpg(D)) echo $i

Method 2

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.

With

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

Then only newline characters are a problem.

Method 3

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.

SCRIPT :

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

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.

Method 4

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' -- {} +

Method 5

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

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

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

echo **/*.jpg


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x