[Novalug] bash weirdness

Mike Miller mtmiller at ieee.org
Fri Apr 9 20:44:03 EDT 2010


On Fri, 2010-04-09 at 16:30 -0400, Ed James wrote: 
> Chris,
> 
>     Try initializing the data[] variable outside/before the while
> loop.
> 
> Ed James
> 
> Quoting "Chris Snyder" <gopher at 3wa.org>:
> 
> > Can somebody please explain to me what this bash snippet is doing?  Why is my
> > array data disappearing outside of the 'while'?  Bash doesn't have scoping by
> > default unless you use local or something like that.  WTF?!?
> >
> > thx
> > Gopher.
> >
> > --------------------
> > #!/bin/bash
> >
> > ls | while read output; do
> >          data[$i]="$output"
> >          let i++
> >          echo "Current array size: " ${#data[*]}
> > done
> >
> > echo "Final array size" ${#data[*]}
> >
> > --------------------
> >
> > Here's my output:
> >
> > bash y
> > Current array size:  1
> > Current array size:  2
> > Current array size:  3
> > Current array size:  4
> > Current array size:  5
> > Current array size:  6
> > Current array size:  7
> > Current array size:  8
> > Current array size:  9
> > Current array size:  10
> > Current array size:  11
> > Current array size:  12
> > Current array size:  13
> > Current array size:  14
> > Current array size:  15
> > Current array size:  16
> > Current array size:  17
> > Current array size:  18
> > Final array size 0

It's actually not scoping or initialization, your command is a pipeline,
where the entire while loop is the second command of the pipeline.  From
the bash man page:

Each command in a pipeline is executed as a separate process (i.e., in a
subshell).

So even if you initialize your array first, the while loop subshell gets
the initial value, changes it inside the loop, and exits.  A subshell
can never change the state of the parent shell.

If you change your while loop to read from a file it should work because
the while loop is not in a subshell now:

ls > foo
while read output; do 
    data[$i]="$output"
    let i++
    echo "Current array size: " ${#data[*]}
done < foo
echo "Final array size" ${#data[*]}

To me, the nicest looking way would be to fill in the "positional
parameters" and process the list like you would an argument list,
something like:

set -- `ls`
while [ "$1" ]; do
    data[$i]="$1"
    let i++
    shift
done

-- 
mike :: mtmiller at ieee dot org




More information about the Novalug mailing list