looping through files with spaces in the filenames in bash

I sometimes have to transfer files with spaces in the names, I like using a for loop, but the usual way doesn't work. Usual way: for file in `find . -type f|grep .ext$` do /do/something/to $file done To get around this I use a while loop with a read instead. Using the read will read to the end of the line, enclosing within quotes escapes the spaces. Unusual way: find . -type f|grep .ext$ |while read file do /do/something/to "$file" done Or if you don't like using more than one line...and enjoy the unreadable (I'm transferring all my avi files to another computer in this example) find . |grep .avi$ |while read file; do scp "$file" 172.16.1.1:; done
Wordpress category: 

Comments

Thank you, thank you, thank you. I've been racking my brain trying to get this to work with a "for file in ..." loop with no success. This helps a lot.

This really really helps.. thank you brotha.

Many thanks for this. I have been writing shell scripts for 20 years and got stuck. I tried many different ways (xargs etc) and was about to resort to a Tcl script.

Thank you so much!

Thanks for that. Just what the doctor ordered.

I have struggled with spaces in pathnames forever using bash. This is like on of the best tips ever...

Thanks a million !

Solved the BBEdit backup folder problem..

find /BACKUP_MAIN/BBEdit_Backups -type f \( -name '*.txt' -o -name '*.applescript' \)|while read file
do
# echo "$file"
mdimport -d 1 /Library/Spotlight/SourceCode.mdimporter "$file"
# mdimport -d 1 /Library/Spotlight/OSAImporter.mdimporter "$file"
done

I had the wrong path in my previous message.

I have struggled with spaces in pathnames forever using bash.

This is like one of the best tips ever…

Thanks a million !

Solved the BBEdit backup folder problem..

find ~/Documents/BBEdit\ Backups -type f \( -name '*.txt' -o -name '*.applescript' \)|while read file
do
# echo "$file"
mdimport -d 1 /Library/Spotlight/SourceCode.mdimporter "$file"
# mdimport -d 1 /Library/Spotlight/OSAImporter.mdimporter "$file"
done

Interstingly this method still fails with spaces at the end of the file. FYI, however I just did the changes maually since it reduced the number of misses from hundreds to 4. Thanks this is better than the environemtn variable that you can change to get the for loop to work.

I've searched before for a solution like this. It's relatively simple, elegant, and I'm pained that it doesn't seem to be more well known. Thanks.

I love you. This has bothered me for ages!

Thanks. In my case, I had a list of filenames, so:

cat list | while read file
do
doSomething $file
done

[...] To loop through files that have spaces in the name, try the following example from narrabilis.com [...]

Awesome -- HUGE thanks!

[...] was soll ich sagen, nach einer stundenlangen Ausprobier-Orgie und Google-Recherchen habe ich hier den passenden Denkanstoß gefunden. #!/bin/bash   find . -name "*.srt" | while read [...]

You could also use the bash input field separator. Just prepend it to whatever your usual way is. Default is all whitespace.

Example:

IFS="
"

for file in `find . -type f|grep .ext$`
do
/do/something/to $file
done

Brilliant. So neat. I just put IFS=^; on the front of my command line and it all just worked.

Thank you! Glad this came up in a google search, was really scratching my head.

Well, what do you say. Tried to solve it my self for an hour or so, then found it on google "I'm feeling lucky". Thank you very much.
My problem: Find files in a directory structure, file names can include spaces, create symbolic names in the current directory.
The files are CA root certificates, link names should be the certificate hash. (a Sendmail + TLS configuration issue)

cd /etc/mailcerts/CA
find . -name *pem -print0 | while read -d $'' file
do
ln -s "$file" `openssl x509 -noout -hash < "$file"`.0
done

For some reasons, qoute characters are missing in my previous post in the code string..

What a simple and elegant solution for something that has bugged me for ages.
Life-Saver!

Yet another person helped by this simple tip. Thanks.

That was awesome, thank you. You only get an A minus because of your unnecessary use of "grep"...;)

Thanks, sure helped me.

[...] was soll ich sagen, nach einer stundenlangen Ausprobier-Orgie und Google-Recherchen habe ich hier den passenden Denkanstoß gefunden. #!/bin/bash   find . -name "*.srt" | while read [...]

5+ years later: thanks for sharing!
I was struggling with quotes to solve the problem, and quotes were winning. You saved me quite some time

I was facing the spaces in file name issue and was lookin for a workaround. this sure solved my problem. thanks !!

...for the useful info!

Cheers

thanks. The tip helped me at a crucial time.

gotta love internet! great post, still useful, helped at the right moment.

As one of the other commenters hinted at, you can get rid of the 'grep' by changing the arguments given to the 'find' command, i.e. replace:
find . |grep .avi$ |while read file; do scp "$file" 172.16.1.1:; done
with:
find . -name "*.avi" |while read file; do scp "$file" 172.16.1.1:; done

And of course since you're using 'find' you could further improve that to:
find . -name "*.avi" -exec scp {} 172.16.1.1: \;

I wrote this a pretty long time ago now, but people still refer to it. This anonymous poster is onto the right idea. I originally didn't do stuff with find because I didn't know it well enough. I still occasionally would do the for loop when I wanted to do more than one thing with the files...then I discovered you can just have multiple exec calls. Meaning you can do a lot with a single line.

find . -name "*.avi" -exec scp {} 172.16.1.1: \; -exec rm {} \;

The ol'

for file in *.ext; do
if [ -f "$file" ]; then
do something
fi
done

Seems to work fine but maybe I'm missing something.

find was just to get things in subdirectories...

Add new comment

Markdown

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Refresh Type the characters you see in this picture. Type the characters you see in the picture; if you can't read them, submit the form and a new image will be generated. Not case sensitive.  Switch to audio verification.