Reputation: 7207
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
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
Reputation: 1806
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
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
Reputation: 84423
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
Upvotes: 2