3

I have a bash script in which I need to iterate over each line of the ouput of the find command, but it appears that I am iterating over each Word (space delimited) from the find command. My script looks like this so far:

folders=`find -maxdepth 1 -type d`

for $i in $folders
do
    echo $i
done

I would expect this to give output like:

./dir1 and foo
./dir2 and bar
./dir3 and baz

But I am insted getting output like this:

./dir1
and
foo
./dir2
and
bar
./dir3
and
baz

What am I doing wrong here?

Lily Mara
  • 3,859
  • 4
  • 29
  • 48

3 Answers3

24
folders=`foo`

is always wrong, because it assumes that your directories won't contain spaces, newlines (yes, they're valid!), glob characters, etc. One robust approach (which requires the GNU extension -print0) follows:

while IFS='' read -r -d '' filename; do
  : # something with "$filename"
done < <(find . -maxdepth 1 -type d -print0)

Another safe and robust approach is to have find itself directly invoke your desired command:

find . -maxdepth 1 -type d -exec printf '%s\n' '{}' +

See the UsingFind wiki page for a complete treatment of the subject.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    Note that the `find ... -exec` form only works for normal commands -- if you want to use shell builtins, shell functions, compound commands, etc you need to use either the `while ... done` approach or the `for` loop chepner suggested. – Gordon Davisson Feb 10 '14 at 01:13
  • @GordonDavisson Generally true. However, the UsingFind wiki page (linked in my answer) describes using `-exec bash -c '...' _ {} +`, which allows shell builtins and the like to be used. – Charles Duffy Feb 10 '14 at 02:02
  • Warning! Some commands, including ffmpeg, will read from stdin, and confuse the "read" command used in the loop. Symptoms are beginnings of the filename being cut off (=> invalid names). You need to disable this behaviour in the command that reads from stdin, two solutions here: https://stackoverflow.com/a/21634699 – Seneral May 09 '23 at 18:15
5

Since you aren't using any of the more advanced features of find, you can use a simple pattern to iterate over the subdirectories:

for i in ./*/; do
    echo "$i"
done
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Most answers talk about using while. But, I was trying to find a solution that involved only a for loop and your solution works great. My scenario was to fetch filename from a different folder. So, added a small change to this : `(cd $folder; for i in ./*/; do echo "$i"; done)`. Created a subshell, for running this on my shell without having to change folder paths – Sarfraaz Ahmed Sep 18 '20 at 18:20
2

You can do something like this:

find -maxdepth 1 -type d | while read -r i
do
    echo "$i"
done
Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69