jperelli
jperelli

Reputation: 7207

What is the correct way in bash to use for i in $(command)?

I was trying to iterate using a for over the output of ls -R, and it just hangs.

for i in $(ls -R /); do echo $i; done;

Is the same for

for i in $(find /); do echo $i; done;

Where is the problem? why keeps "the cursor waiting"?

Upvotes: 0

Views: 315

Answers (4)

cdarke
cdarke

Reputation: 44394

It depends what you want to do inside the loop. The problem with piping into a while loop is that any variable changes inside the loop are not visible outside. For example:

x=0
ls -R / | while read i; do
    (( x++ ))
done
echo $x

Will give 0 (by the way, ksh does not have this issue). A solution is to use Process Substitution, which uses a named pipe to feed one line at a time, and this time the loop runs in-line:

x=0
while read i; do
    (( x++ ))
done < <(ls -R /)
echo $x

ksh93 also supports this syntax

Upvotes: 1

It's because the variables are read, loaded into for.

Sometimes, bash just can't internally handle those many variables. Try to use these alternatives for best results.

Try these instead:

#IO based is best.
ls -R / | while read i ; do
  echo $i
done

(or)

#in your case
find / -exec echo \{\} \;

Upvotes: 1

Stephen Weinberg
Stephen Weinberg

Reputation: 53478

The reason it pauses is because first the $() is treated as a variable to be calculated first. So it first does a find and loads all that into memory and then iterates over it.

Looping over a find tends to be a bad idea anyways. Even for small numbers of items. The bash for loop considers all whitespace to be delimiters for items. This means you will get undesirable results if any of your filenames contain spaces. Instead, I suggest using find's built-in exec option.

find / -exec echo '{}' \;


As Karthik pointed out, you can also use a while loop with read.

ls -R / | while read i; do
    echo $i
done

This loop works for two reasons. First, read breaks on \n (or EOF) only. This means that files with whitespace will be counted as one file. Second, the pipe ensures that data does not build up. Filenames will be eaten by the loop as they are produced by ls.

Upvotes: 2

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84423

An Answer Other Than "Find"

It's generally a bad idea to parse the output of ls. Other folks have already shown you how to use find. You have another option in bash: globstar.

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

See Also

Upvotes: 2

Related Questions