Bash problem with assigning an index to an array in a loop

I can get this to work in ksh, but not in bash, which really gets me nuts. I hope this is something obvious that I do not notice.

I need to run an external command where each line of output will be stored in an array index.

This simplified example looks like it sets the array in the loop correctly, but after the loop has finished executing these arrays? As if the loop is being processed completely like an outer shell?

junk.txt

this is a test to see if this works ok 

testa.sh

 #!/bin/bash declare -ii=0 declare -a array echo "Simple Test:" array[0]="hello" echo "array[0] = ${array[0]}" echo -e "\nLoop through junk.txt:" cat junk.txt | while read line do array[i]="$line" echo "array[$i] = ${array[i]}" let i++ done echo -e "\nResults:" echo " array[0] = ${array[0]}" echo " Total in array = ${#array[*]}" echo "The whole array:" echo ${array[@]} 

Exit

 Simple Test: array[0] = hello Loop through junk.txt: array[0] = this is a array[1] = test to see array[2] = if this works ok Results: array[0] = hello Total in array = 1 The whole array: hello 

So, while in the loop we assign array [i], and the echo checks it. But after the loop, I will return to the array [0] containing "hello" without any other elements.

The same results in bash 3, 4 and different platforms.

+6
source share
1 answer

Since your while loop is in the pipeline, all variable assignments in the body of the loop are local to the subshell in which the loop is executed. (I believe that ksh does not run the command in a subshell, so you have a problem in bash .) Do this instead:

 while read line do array[i]="$line" echo "array[$i] = ${array[i]}" let i++ done < junk.txt 

Rarely, if ever, do you want to use cat to join one file with another command; use input redirection instead.

UPDATE: since you need to execute the command from the command line and not the file, another option (if available) is to replace the process:

 while read line; do ... done < <( command args ... ) 

If process substitution is not available, you will need to output to a temporary file and redirect input from this file.

If you are using bash 4.2 or later, you can execute these two commands before your loop, and the original pipe-in-loop will work, since the while loop is the last command in the pipeline.

 set +m # Turn off job control; it probably already off in a non-interactive script shopt -s lastpipe cat junk.txt | while read line; do ...; done 

UPDATE 2: This is a no-loop solution based on user comment1596414

 array[0]=hello IFS=$'\n' array+=( $(command) ) 

The output of your command is broken down into words based solely on newline characters (so that each line is a separate word) and adds the resulting array of lines to the slot in the original. This is very good if you use a loop only to build an array. It can also be modified to accommodate a small amount of processing on each line, vaguely similar to understanding a Python list.

+7
source

Source: https://habr.com/ru/post/922775/


All Articles