3

Consider this example, say, test.sh:

cat > test.txt <<'EOF'
test 1
test 2
test 3
EOF

declare -a myarr
declare -p myarr  # "declare: myarr: not found"
myarr=()
declare -p myarr  # "declare -a myarr='()'"
#for (( i=1; i<=3; i++ )); do # ok
sed -n 's!test!TEST!p' test.txt | while read i; do # not preserved ?!
  myarr=("${myarr[@]}" "pass $i")
  declare -p myarr
done
declare -p myarr  # "declare -a myarr='()'" ?!

If I uncomment the for ((... line, and comment the sed -n ... line, then the output of bash test.sh is as expected:

test.sh: line 8: declare: myarr: not found
declare -a myarr='()'
declare -a myarr='([0]="pass 1")'
declare -a myarr='([0]="pass 1" [1]="pass 2")'
declare -a myarr='([0]="pass 1" [1]="pass 2" [2]="pass 3")'
declare -a myarr='([0]="pass 1" [1]="pass 2" [2]="pass 3")'

However, if I run the script as posted, then the myarr builds in the while loop, but once outside, it's empty:

test.sh: line 8: declare: myarr: not found
declare -a myarr='()'
declare -a myarr='([0]="pass TEST 1")'
declare -a myarr='([0]="pass TEST 1" [1]="pass TEST 2")'
declare -a myarr='([0]="pass TEST 1" [1]="pass TEST 2" [2]="pass TEST 3")'
declare -a myarr='()'

So, why is myarr in this case (in, or rather, after the while loop) empty - and how do I get it to preserve its value?

1

1 Answer 1

8

Problem is that due to use of pipeline, you are forking a subshell and populating entries in your array inside the subshell. Once your loop ends, subshell terminates and all the changes get lost there.

You can use a process substitution to avoid this:

myarr=()

while IFS= read  -r line; do
   myarr+=("pass $line")
done < <(sed -n 's!test!TEST!p' test.txt)

# examine the result
declare -p myarr

Output:

declare -a myarr=([0]="pass TEST 1" [1]="pass TEST 2" [2]="pass TEST 3")
Sign up to request clarification or add additional context in comments.

2 Comments

Many thanks @anubhava -> that was indeed the issue! Cheers!
I prefer using other forms such as exec {fd}< <(sed ...); while read -r -u $fd line; do ...; done; exec {fd}<&- because it prevents accidentally consuming the same source within the loop, but this is the right way to do it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.