2

Very new to Bash and pretty confused about local/global variables/subshells. I'm not sure why the modified variables won't print out at the end of the function—I'm trying to print out a final line count and file count at the end of the file, but if I do, it only prints out 0 because they are local variables. Is there any way to print out the modified values?

count=0
files=0
find . -type f | while IFC= read -r file;
do
   let files=files+1
   wc -l $file
   count=$(($count+$(wc -l < $file)))
   echo "total lines $count ; total files $files"
done
echo $files $count
exit 0
novel
  • 23

1 Answers1

2

Yes. But it is absolutely non-intuitive. This will work, for instance:

#!/bin/bash
count=0
files=0
while IFS= read -r file;
do
   let files=files+1
   wc -l $file
   count=$(($count+$(wc -l < $file)))
   echo "total lines $count ; total files $files"
done < <(find . -type f )
echo "$files $count"
exit 0

The <(command) construct is called "process substitution" and lets you treat the output of a command as a "file". Feeding it into the loop this way makes your script work as you expect.

The problem is your use of the pipe (|) which causes the while loop to run in a separate subshell which can't modify variables outside it.

In shells that don't support the <() feature, you can run the command(s) on the right of the pipe in a subsell and include the final echo in that subshell:

#!/bin/bash
files=0
find . -type f | {
    while IFC= read -r file;
    do
        let files=files+1
        wc -l $file
        count=$(($count+$(wc -l < $file)))
        echo "total lines $count ; total files $files"
    done
    echo "$files $count"
}

exit 0
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
terdon
  • 242,166