LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   How to define a variable In a while loop? (https://www.linuxquestions.org/questions/programming-9/how-to-define-a-variable-in-a-while-loop-500481/)

fatsheep 11-10-2006 08:47 PM

How to define a variable In a while loop?
 
I'm writing a bash package management script and I have a variable, $conflict, that is set to "1" if a system-package conflict exists (the system contains the same files that the package does). Here's the loop where the script checks for conflicts:

Code:

#### CHECK FOR PACKAGE - SYSTEM CONFLICTS
#### Set $conflict to "1" if a conflict exists.

find | \
# Outputs all files and directories under the PWD.
sed "/\.$/d" | \
# Deletes the current directory (.) entry.
sed "s@^.@@" | \
# Removes the "." char from the beginning of all the file paths

while read file ; do
echo 'Entering Loop #1'

#echo "\$file = $file"

sysFile=`echo $file | sed "s@^@$sysRoot@"`
pkgFile=`echo $file | sed "s@^@$pkgPath@"`

#echo '$sysFile =' $sysFile
#echo '$pkgFile =' $pkgFile

if [ -d $pkgFile ] && [ -e $sysFile ] && ! [ -d $sysFile ] ; then

        echo "ERROR: $sysFile exists!"
        conflict="1"
        break

fi


if ! [ -d $pkgFile ] && [ -e $sysFile ] ; then
               
        echo "ERROR: $sysFile exists!"
        conflict="1"
        break
       
fi

echo 'Exiting Loop #1'

echo $conflict
done

My problem is that when I set $conflict to "1", it only remains "1" within the loop. Right after the break command, it returns to the default value which I defined at the beginning of the script: "0". How would I make a global change to the variable (meaning that it will = "1" throughout the script)?

dxqcanada 11-10-2006 09:17 PM

Shouldn't the
echo $conflict
be outside of the while loop ?

fatsheep 11-10-2006 09:25 PM

Quote:

Originally Posted by dxqcanada
Shouldn't the
echo $conflict
be outside of the while loop ?

There's one outside of the loop as well, I just didn't include that segment.

dxqcanada 11-10-2006 09:32 PM

Since you are breaking out of the loop the echo $conflict that is in the loop will show "0" (since conflict only get set to 1 before breaking out of the loop.

The echo $conflict that is outside of the loop should show "1" if the condition is met.

This simple script will show you"

Code:

#!/bin/bash
conflict=0
while read input
do
if [ $input = 0 ]
then
conflict=1
break
fi
echo conflict inside is $conflict
done
echo conflict outside is $confict


kr4m3r 11-10-2006 09:33 PM

Perhaps try to "define" the variable before the loop?

duryodhan 11-10-2006 09:58 PM

ya I guess, just do conflict=0 before beginning of while loop.

fatsheep 11-11-2006 11:43 AM

Quote:

Originally Posted by dxqcanada
Since you are breaking out of the loop the echo $conflict that is in the loop will show "0" (since conflict only get set to 1 before breaking out of the loop.

The echo $conflict that is outside of the loop should show "1" if the condition is met.

This simple script will show you"

Code:

#!/bin/bash
conflict=0
while read input
do
if [ $input = 0 ]
then
conflict=1
break
fi
echo conflict inside is $conflict
done
echo conflict outside is $confict


That example returned errors... Not sure why but here's my edited code:

Code:

#!/bin/bash

# $conflict will be set to "1" if conflict exists
conflict="0"
echo $conflict

sysRoot="/home/ubuntu/Desktop/sources/sysRoot"
rootPkgDir="/home/ubuntu/Desktop/sources/pkg"
pkg="lynx-2.8.6"
pkgPath="$rootPkgDir/$pkg"
#pkgPath="/home/ubuntu/Desktop/sources/pkg/lynx-2.8.6"

echo '$rootPkgDir = ' $rootPkgDir
echo '$pkg = ' $pkg
echo '$pkgPath = ' $pkgPath

cd $pkgPath




#### CHECK FOR PACKAGE - SYSTEM CONFLICTS
#### Set $conflict to "1" if a conflict exists.

find | \
# Outputs all files and directories under the PWD.
sed "/\.$/d" | \
# Deletes the current directory (.) entry.
sed "s@^.@@" | \
# Removes the "." char from the beginning of all the file paths

while read file ; do
#echo 'Entering Loop #1'

#echo "\$file = $file"

sysFile=`echo $file | sed "s@^@$sysRoot@"`
pkgFile=`echo $file | sed "s@^@$pkgPath@"`

#echo '$sysFile =' $sysFile
#echo '$pkgFile =' $pkgFile

if [ -d $pkgFile ] && [ -e $sysFile ] && [ ! -d $sysFile ] ; then

        echo "ERROR#1: $sysFile exists."
        conflict="1"
        echo "Setting \$conflict to \"1\""
        echo $conflict
        break

fi


if [ ! -d $pkgFile ] && [ -e $sysFile ] ; then
               
        echo "ERROR#2: $sysFile exists."
        conflict="1"
        echo "Setting \$conflict to \"1\""
        echo $conflict
        break
       
fi

#echo 'Exiting Loop #1'

done

echo '$conflict =' $conflict "outside the loop."

Here's the output when I run the script:

Quote:

0
$rootPkgDir = /home/ubuntu/Desktop/sources/pkg
$pkg = lynx-2.8.6
$pkgPath = /home/ubuntu/Desktop/sources/pkg/lynx-2.8.6
ERROR#2: /home/ubuntu/Desktop/sources/sysRoot/usr/bin/lynx exists.
Setting $conflict to "1"
1
$conflict = 0 outside the loop.
as you can see, the second if statement evaluates as true ( I have set up a conflict on purpose to test it). Then $conflict is set to "1". I echo conflict inside the if statement, it returns the value "1". Then I issue the break command to exit the loop. Outside the loop, I echo $conflict, this time it evaluates as "0". Why?

ygloo 11-11-2006 06:39 PM

it seems to me that variable keeps its value within the loop...
try "export $conflict " and echo $conflict ( to see the value)... inside the loop

randyding 11-11-2006 06:51 PM

The problem is the pipe. When you pipe a new instance of the shell is executed as a separate process to receive data on stdin. When you make a variable assignment inside this new pipe process the variable is lost when the process exits at the end of the pipe.

ygloo 11-11-2006 07:21 PM

Quote:

My problem is that when I set $conflict to "1", it only remains "1" within the loop. Right after the break command, it returns to the default value which I defined at the beginning of the script: "0". How would I make a global change to the variable (meaning that it will = "1" throughout the script)?
try declare -r conflict=1

"
-r Make variables read-only. These variables cannot then be assigned values by subsequent assignment statements, nor can they be unset.
"

randyding 11-12-2006 09:24 AM

Here's a short example that demonstrates your situation.
Code:

#!/bin/bash

linenum=0
echo -en "hello\nworld\n" | while read x; do
    linenum=$((linenum+1))
    echo "$linenum: $x"
done
echo "total number of lines = $linenum"

It then prints out...
1: hello
2: world
total number of lines = 0

The problem is you want the total number of lines to be printed as 2. This can not be done with a variable assignment in the loop because the pipe ( | while read ...) runs in a separate process.

aluser 11-12-2006 02:36 PM

A workaround is to put the entire "stuff | while blah; do halb; done" construct inside backquotes and echo $conflict at the end. i.e.
Code:

conflict=`stuff | ( while blah; do whatever; done; echo $conflict )`
Be sure to echo any errors found inside the subshell to stderr like
Code:

echo >&2 "Oh no Mr. Bill!"
so that they aren't captured by the backquotes

hpal 03-01-2012 01:12 AM

We have the same problem about variable scoping, since I also need to get the presently login user and then, check if corresponding user belongs to a group.

I have wrote several test code with regards to variable scoping outside while loop. But, I could not find any related variable declaration to make the variable value within WHILE loop, I also use export and using extra conditions. The result leads me to conclude that variable within While Loop is only accessible from within the loop itself.

So, I decided to save the value to a file then read it back outside the while loop -- it resolves my problem, please see below code if you could find it useful:


whoami | while read line; do
#if [[ "$userName" = "" ]] ; then
userName=$line
#fi
echo $userName
if [ ! $userName == "" ]; then
echo "userName=$userName" > ~/getuser.bash
break
fi
done
chmod 755 ~/getuser.bash
echo "username value outside while loop=$userName"

if [ -f getuser.bash ] ; then
source getuser.bash
fi
echo "username value outside while loop in getuser.bash file=$userName"

Output:
username value outside while loop=
username value outside while loop in getuser.bash file=hpal

rknichols 03-01-2012 03:56 PM

@hpal -- You are responding to a thread that is over 5 years old. The OP either solved his problem or gave up long ago.

bigearsbilly 03-02-2012 10:40 AM

or dead.
but FYI use export:

Code:

$ export x=fish
$ echo $x
fish
$ while :;do x=chips;break;done
$ echo $x
chips


ntubski 03-02-2012 04:09 PM

export passes variables to subprocesses, but that doesn't help getting the data back to the parent:
Code:

$ export x=fish
$ echo $x
fish
$ whoami | while :;do x=chips;break;done
$ echo $x
fish

The problem is explained in detail here: I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?

hatalbot 07-30-2014 01:12 PM

Another way to fix the problem is to place the input into a file,
then redirection to have the while loop process the file

i.e.

-----------------------------------------------------------
Doesn't work due to pipe causing subprocess being created:
-----------------------------------------------------------
export conflict=0
cat $filename | while read inline
do
conflict=1
done

echo $conflict # get 0

--------------------
WORKS!
--------------------
export conflict=0
while read inline
do
conflict=1
done < $filename

echo $conflict # get 1 !

Since no pipe, no subprocess created, values in while loop retained


All times are GMT -5. The time now is 07:26 AM.