Variables lose their values after loop

Viewed 61

I have this script which supposed to read file with numbers in each line, and show the largest number, the smallest one and the sum. During the loop variables change their values but after they return their primary value and I can't fix it.

#!/bin/bash

if [ ! $# -eq 1 ]; then
    echo "Invalid number of arguments"
elif [ ! -e $1 ]; then
    echo "File doesn't exist"
elif [ ! -s $1 ]; then
    echo "File is empty"
else
    min=$(head -1 $1)
    max=$(head -1 $1)
    sum=0
    (while read i; do
        (( sum+=i ))
        if [ $min -gt $i ]; then
            min=$i
        elif [ $max -lt $i ]; then
            max=$i
        fi
    done
    )<$1
fi
echo $min $max $sum
2 Answers
(while read i; do
    (( sum+=i ))
    if [ $min -gt $i ]; then
        min=$i
    elif [ $max -lt $i ]; then
        max=$i
    fi
done
)<$1

The subshell introduced by surrounding the loop with ( ) parentheses causes all the variable modifications inside to be confined to the subshell. A subshell is a child process and child processes have their own copies of variables separate from the parent process's.

while read i; do
    (( sum+=i ))
    if [ $min -gt $i ]; then
        min=$i
    elif [ $max -lt $i ]; then
        max=$i
    fi
done <$1

The subshell's not doing anything useful so the easy solution is to remove it.

I changed the expression in top line of loop to cat $1|while read i and removed brackets but it didn't help.

Pipelines have the same problem as subshells: the left and right sides run in child processes. Stick with the while ... done <file version with no pipeline and no subshell.

The problem is that in Bash, parentheses create a new subshell. You're creating the variables in that subshell, and those variables go away when the subshell finishes.

$ i=foo; (i=bar; echo $i); echo $i
bar
foo

If you remove the parens from your loop, it works:

while read i; do
     (( sum+=i ))
     if [ $min -gt $i ]; then
         min=$i
     elif [ $max -lt $i ]; then
         max=$i
     fi
done < $1
Related