Behavior of bash for loop

I have 2 files containing spaces in the filename.

a 1 1.txt
b 2 2.txt

The follow bash script would produce the following output:

$ for i in $(find); do echo “$i”; done
.
./a
1
1.txt
./b
2
2.txt

Whereas this script produces the right output:

$ for i in “$(find)”; do echo “$i”; done
.
./a 1 1.txt
./b 2 2.txt

Could someone explain to me why the behavior is that shown?
Thanks in advance.

buzlite wrote:

>
> I have 2 files containing spaces in the filename.
>
> a 1 1.txt
> b 2 2.txt
>
> The follow bash script would produce the following output:
>
> $ for i in $(find); do echo “$i”; done

Lots of things to consider for files with special characters and/or spaces.

Consider:

find . -print0
combined with
xargs -0

Consider:

using filters to process the output of find into what you ware wanting to do

Consider:

using read to process line by line output (albeit slowly)

There are LOTS of ways to look at the problem. Without seeing more of the
code, it’s hard to determine the best solution.

You must deal with filenames by using quotations around them, else it will appear in a bash file to be more than one augment, possibly treated as multiple file names. I have run into the very same problem in scripts I have written which require the name of a file to be provided. Without the quotes, filenames with spaces are dealt with as multiple augments though such behavior is not totally consistent. Use the quotes in your program and all will be well and there is no extra charge to use them.

Thank You,

you use quotes so that it will retain the spaces. And why use for loop and find? Just use shell expansion


#!/bin/bash
shopt -s nullglob
for file in *
do
  echo "$file"
done

don’t do the unnecessary.

Well, * isn’t quite equivalent to $(find) as find returns files in subdirectories also.

Try something like this:

find | while read f
do
   echo "$f"
done

Of course if you have files with newlines in the name, that would fail also. :slight_smile:

i believe the OP doesn’t know any better. And that find is not what he intended to use. In bash 4, we can use **


shopt -s globstar
for file in **
do
  echo "$file"
done

Try something like this:

find | while read f
do
   echo "$f"
done

better to use -r


while read -r f

Of course if you have files with newlines in the name, that would fail also. :slight_smile:

like specifying -d ‘$\0’ or something like that…

Interesting, learn something new everyday. Could come in useful.