13

I try to initialize an array in bash-4.2 next way:

ring=()
ls -las | tail -n +4 | while read line
> do
> ring+=("$line")
> echo ${ring[-1]}
> done
3924 -rw-r--r-- 1 username group 4015716 Mar 23 15:14 script.jar
4 -rw-r--r-- 1 username group 9 Feb 29 12:40 rec.lst
5541 -rw-r--r-- 1 username group 5674226917 Mar 28 15:25 debug.out
8 -rw-r--r-- 1 username group 6135 Mar 25 12:16 script.class
8 -rw-r--r-- 1 username group 6377 Mar 25 11:57 script.java
8 -rwxr-xr-x 1 username group 4930 Mar 8 15:21 script-0.0.0.sh
8 -rwxr-xr-x 1 username group 6361 Mar 28 15:27 script-0.0.1.sh
echo ${ring[0]}

echo "${ring[0]}"

echo "${ring[@]}"

What is wrong, why i get empty array after the finish of the loop?

Dimaf
  • 243
  • 1
    echo $BASH_SUBSHELL at various points might be instructive to see whether there is any subshell action going on - http://tldp.org/LDP/abs/html/internalvariables.html#BASHSUBSHELLREF – thrig Mar 28 '16 at 20:12

3 Answers3

16

Your problem is that in a pipeline ( command1 | command2 | command3 ... ) the commands are ran in subshells. Variables are not shared between subshells or between subshells and the main shell. The ring in the while loop is different than the ring in the main shell.

One way to overcome this is to use process substitution:

while read line;  do  ring+=("$line");  echo ${ring[-1]};  done < <(ls -las|tail -n +4) 

The <(command) syntax is called process substitution and will redirect the output of the command to a named pipe. Which is then redirected with the familiar < as if it was a file. When you use <, there is no subshell, so the ring variable will be set.

Note that there is a shell builtin command to fill an array from the lines of a file:

mapfile -t ring < <(ls -las | tail -n +4)
RealSkeptic
  • 1,130
3

This should work fine:

ring=()
while read line
do
ring+=("$line")
echo ${ring[-1]}
done < <(ls -las | tail -n +4)

Source: http://wiki.bash-hackers.org/syntax/arrays

0

The reason is while loop is executed in a subshell due to the fact it is piped. Subshell uses a copy of the parent shell environment and does not pass it back when the subshell quits.

for bash you can use command grouping workaround, note curly brackets added

ls -las | tail -n +4 | { while read line;  do  ring+=("$line");  echo ${ring[-1]};  done;  echo ${ring[0]}; }
Tagwint
  • 2,480
  • This doesn't solve the problem - the value may be echoed, but the array will still be uninitialized at the end. – RealSkeptic Mar 28 '16 at 20:21
  • @RealSkeptic it depends on what considered end. The further code just should stay in the brackets to be able to use the array. The problem was array was empty after the loop. Wtih brackets it is not anymore. – Tagwint Mar 28 '16 at 20:26