Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shell variables set inside while loop not visible outside of it

Tags:

bash

I am trying to find the pathname with the most characters in it. There might be better ways to do this. But I would like to know why this problem occurs.

LONGEST_CNT=0 find samples/ | while read line do     line_length=$(echo $line | wc -m)     if [[ $line_length -gt $LONGEST_CNT ]]      then         LONGEST_CNT=$line_length         LONGEST_STR=$line     fi done  echo $LONGEST_CNT : $LONGEST_STR 

It somehow always returns:

0 : 

If I print the results for debugging inside the while loop the values are correct. So why bash does not make these variables global?

like image 242
Mark Avatar asked Jan 12 '11 10:01

Mark


People also ask

Where are shell variables stored?

Shell variables are stored in the memory of the running shell. Use any data structure that lets you easily look up an item given its name; a hash table is a good choice. The difference between shell variables and environment variables is that environment variables are placed in the environment of subprocesses.

Why shell does not have do while loop?

bash (or Posix shells in general) don't have an explicit syntax for a post-test loop (commonly known as a "do-while" loop) because the syntax would be redundant.

How do you break out of a loop in shell?

break exits from a for, select, while, or until loop in a shell script. If number is given, break exits from the given number of enclosing loops. The default value of number is 1 . break is a special built-in shell command.


2 Answers

When you pipe into a while loop in Bash, it creates a subshell. When the subshell exits, all variables return to their previous values (which may be null or unset). This can be prevented by using process substitution.

LONGEST_CNT=0 while read -r line do     line_length=${#line}     if (( line_length > LONGEST_CNT ))     then         LONGEST_CNT=$line_length         LONGEST_STR=$line     fi done < <(find samples/ )    # process substitution  echo $LONGEST_CNT : $LONGEST_STR 
like image 129
Dennis Williamson Avatar answered Sep 19 '22 04:09

Dennis Williamson


The "correct" reply is given by Dennis. However, I find the process substitution trick extremely unreadable if the loop contains more than a few lines. When reading a script, I want to see what goes into the pipe before I see how it is processed.

So I usually prefer this trick of encapsulating the while loop in "{}".

LONGEST_CNT=0 find /usr/share/zoneinfo | \ { while read -r line     do         line_length=${#line}         if (( line_length > LONGEST_CNT ))         then             LONGEST_CNT=$line_length             LONGEST_STR=$line         fi     done     echo $LONGEST_CNT : $LONGEST_STR } 
like image 40
mivk Avatar answered Sep 20 '22 04:09

mivk