Array disappears in bash script

I am writing a script to collect various network statistics. What I'm trying to do is create some delta data from the netstat -i command.

I am collecting the necessary data with the following bash code:

declare -a array n=0 netstat -i | tail -n +3 | while read LINE; do echo "Setting array[$n] to $LINE" array[$n]=$LINE echo "array now have ${#array[@]} entries" let n=$n+1 done echo "array now have ${#array[@]} entries" 

output from this command:

 Setting array[0] to eth0 1500 0 4946794 0 0 0 2522971 0 0 0 BMRU array now have 1 entries Setting array[1] to lo 16436 0 25059 0 0 0 25059 0 0 0 LRU array now have 2 entries Setting array[2] to vmnet1 1500 0 6 0 0 0 1126 0 0 0 BMRU array now have 3 entries Setting array[3] to vmnet8 1500 0 955 0 0 0 1054 0 0 0 BMRU array now have 4 entries Setting array[4] to wlan0 1500 0 613879 0 0 0 351194 0 0 0 BMU array now have 5 entries array now have 0 entries 

As you can see, the array actually disappears after the while loop, and I don't understand why.

+4
source share
3 answers

Each time you use a channel, you create an implicit subshell. When this subshell terminates, so does its variables. A quick fix is ​​to not pass stuff to read . You can accomplish the above using process substitution:

 while read LINE; do echo "Setting array[$n] to $LINE" array[$n]=$LINE echo "array now have ${#array[@]} entries" let n=$n+1 done < <(netstat -i | tail -n +3) 

A more POSIX-compatible approach (read: more portable, less basti) so that everything happens in a subshell:

 netstat -i | tail -n +3 | { declare -a array n=0 while read LINE; do echo "Setting array[$n] to $LINE" array[$n]=$LINE echo "array now have ${#array[@]} entries" let n=$n+1 done echo "array now have ${#array[@]} entries" } 

You can read the subtleties of this (and more) in the Greg Wooledge wiki .

+4
source

If your only goal is to output the command output to an array (linewise), you better use the (unfortunately not very well known) mapfile bash built-in, it is by far the most efficient (and best suited for code golf, count the number of character strokes, which I compared with other features):

 mapfile -t array < <(netstat -i | tail -n +3) 

Other answers explain why your design did not work (the pipe is in a subshell and all that).

help mapfile for all the details and features of this command.

+2
source

Ok, are you ready?

How to convert netstat -i | tail -n +3 netstat -i | tail -n +3 to the associative array of the bash array:

 declare -A AANET while read -a line ;do declare -a AI$line eval "AI$line=(${line[@]})" AANET[$line]=AI$line done < <( netstat -i | tail -n +3) 

Than now:

 echo ${!AANET[@]} venet0 eth1 eth0 lo br0 echo ${AANET[eth0]} AIeth0 

And for subassociative we should use eval :

 eval echo \${${AANET[eth0]}[@]} eth0 1500 0 17647 0 0 0 35426 0 0 0 BMPU eval echo \${${AANET[eth0]}[1]} 1500 eval echo \${${AANET[eth0]}[3]} 17647 eval echo \${${AANET[eth0]}[7]} 35426 eval echo \${${AANET[eth0]}[@]:3:5} 17647 0 0 0 35426 

An to define a temporary variable:

 eval currentBin=\${${AANET[eth0]}[3]} currentBout=\${${AANET[eth0]}[7]} echo $currentBout 35426 echo $currentBin 17647 

or even too much:

 eval "declare -a currentVals=(\${${AANET[eth0]}[@]:3:8})" echo ${currentVals[0]} 17647 echo ${currentVals[4]} 35426 echo ${currentVals[@]} 17647 0 0 0 35426 0 0 0 

Edit

Well, if possible without eval !

 for aKey in ${!AANET[@]};do fields=(${AANET[$aKey]}{[1],[3],[7]}); echo $aKey ${!fields} ${!fields[1]} ${!fields[2]} done | xargs printf "%-9s %12s %12s %12s\n" IFace MTU RX TX IFace MTU RX TX venet0 1500 0 0 eth1 1500 6400292 6942577 eth0 1500 17647 35426 lo 16436 83 83 
+1
source

All Articles