59

I have a script that process a folder, and count the files in the mean time.

i=1
find tmp -type f | while read x
do 
   i=$(($i + 1))
   echo $i
done
echo $i

However, $i is always 1, how do I resolve this?

daisy
  • 53,527
  • 78
  • 236
  • 383
  • Also see: [A variable modified inside a while loop is not remembered on Stack Overflow](https://stackoverflow.com/q/16854280/2072269) – muru Nov 06 '17 at 04:10

2 Answers2

75

In your example the while-loop is executed in a subshell, so changes to the variable inside the while-loop won't affect the external variable. This is because you're using the loop with a pipe, which automatically causes it to run in a subshell.

Here is an alternative solution using a while loop:

i=1
while read x; do
   i=$(($i + 1))
   echo $i
done <<<$(find tmp -type f)
echo $i

And here is the same approach using a for-loop:

i=1
for x in $(find tmp -type f);
do 
   i=$(($i + 1))
   echo $i
done
echo $i

For more information see the following posts:

Also look at the following chapter from the Advanced Bash Scripting Guide:

igal
  • 9,666
  • 1
  • 42
  • 58
  • 2
    regarding the first solution, isn't it process substitution like so `< <(find ...)` instead of like so `<<<(find ...)` .... yours is the latter and that seems incorrect. – Alexander Mills Jun 22 '18 at 03:47
  • @AlexanderMills The `<<<` operator is called a [here-string](http://www.tldp.org/LDP/abs/html/x17837.html). – igal Feb 17 '19 at 22:11
  • 1
    solution using for-loop doent work if the output contains spaces; but while-loops works – Pragalathan M Sep 23 '19 at 10:54
  • The "for" loop is the answer I've been looking for! :) – fig Aug 31 '21 at 10:38
  • 1
    A simpler version of `<<<$(` is `< <(`. Using here documents causes problems. E.g. they ignore NULL bytes, like when the command `find … -print0` is used. – Evi1M4chine Jun 13 '22 at 23:20
  • This answer solves the immediate issue of allowing `i` to survive past the end of the loop, but does not address the other issues still in the code. Also, note that if the user wants the last value found by `find`, there are better ways to do this that would not break due to the presence of whitespaces in pathnames. – Kusalananda Mar 25 '23 at 14:58
9
i=1
while read x
do
   i=$(($i + 1))
   echo $i
done < <(find tmp -type f)
echo $i

https://stackoverflow.com/questions/7390497/bash-propagate-value-of-variable-to-outside-of-the-loop

Kamaraj
  • 4,295
  • 1
  • 12
  • 18
  • This would solve the immediate issue of allowing the variable `i` to survive past the end of the loop, but the answer does not explain why this works. Also, correcting the many _other_ issues still present in the code would be trivial. – Kusalananda Mar 25 '23 at 14:53