0

I have an array with some data:

array1=( AAA BBB CCC DDD )

I want to populate an array of results from calling a certain API with the data in array1 and at the same time I want to show the progress with zenity. So I though about doing this:

i=0
prog=0

for c in ${array1[@]}; do
  echo $prog  #updates the text
  echo "# $c" #updates the percentage

  data_array[$i]=$(curl -s "https://hub.dummyapis.com/products?noofRecords=4&idStarts=1001&useless=$c" | jq .[$i].id | bc)

  (( prog=prog+30 ))
  (( i++ ))
done | zenity \
    --progress \
    --title="Title" \
    --text="Text" \
    --percentage=0 \
    --auto-close \
    --auto-kill

The problem is that the data_array remains empty.

On the other hand, it gets populated if I omit the pipe to the zenity command. If I understood correctly, it's because the pipe it's spawning a new subprocess, thus the data_array is empty there.

I also tried using this sintax, but with same results:

zenity \
    --progress \
    --title="Title" \
    --text="Text" \
    --percentage=0 \
    --auto-close \
    --auto-kill < <(
for c in ${array1[@]}; do
  echo $prog
  echo "# $c"

  data_array[$i]=$(curl -s "https://hub.dummyapis.com/products?noofRecords=4&idStarts=1001&useless=$c" | jq .[$i].id | bc)

  (( prog=prog+30 ))
  (( i++ ))
done)

What can I do?

2
  • 1
    In both cases, data_array is in the scope of a sub-process, not the outer process. The two parts of a pipeline run in sub-processes. The $(..) runs in a sub-process. Commented Nov 14, 2021 at 20:25
  • 2
    @Paul_Pedant nitpick: the first part (or in general all but the last part) of a pipeline always runs in subshell(s); the last part in general may run in a subshell or the main shell, and for bash it runs in the main shell if job control is off and shopt LASTPIPE is on. That makes a difference for (fairly numerous) questions of the form something | while read stuff; do set some vars; done, but not this one. Commented Nov 15, 2021 at 0:57

3 Answers 3

1

The parts of a pipeline run in subshells. (In Bash, you can enable the lastpipe option to have the last part run in the main shell, but that doesn't help here.) Process substitutions also run in subshells, but they don't force main part to do so too.

Put zenity in a process substitution instead and keep the for in the main shell:

for c in ${array1[@]}; do
  ...
  data_array[$i]=...
  ...
done > >( zenity \
    --progress \
    --title="Title" \
    --text="Text" \
    --percentage=0 \
    --auto-close \
    --auto-kill )
1
  • Yep, this is it... I kinda recognized I needed a process substitution, but I swapped the two parts of the script ':D Commented Nov 16, 2021 at 8:24
1

Another possibility: Replace in your first example

data_array[$i]=$(curl ... | jq ... | bc)

with

curl ... | jq ... | bc >> /tmp/so-q

and add after your code:

mapfile -t data_array < /tmp/so-q
rm /tmp/so-q
declare -p data_array

To create secure temporary files you can use mktemp. See man mktemp.

0

Maybe it's not the most elegant solution, but it works.

It relies on tee for the output of the loop to be written to a file and piped into zenity at the same time.

Using the dummy code above, it become:

array1=( AAA BBB CCC DDD )

curl_cmd() {
  curl -s "https://hub.dummyapis.com/products?noofRecords=4&idStarts=1001&useless=$1" | jq .[$2].id | bc
}

i=0
prog=0

for c in ${array1[@]}; do
  #updates the text
  echo $prog
  #updates the percentage
  echo "# $c"

  #echo the data I'm interested in, 
  #with a @ for later reference
  echo "@ $(curl_cmd $c $i)" 

  (( prog=prog+30 ))
  (( i++ ))
done | tee >(zenity \
    --progress \
    --title="Title" \
    --text="Text" \
    --percentage=0 \
    --auto-close \
    --auto-kill) > /tmp/so-q

data_array=( $(grep @ /tmp/so-q | cut -c 2-) )

Now the data_array is finally populated

~ $ echo ${data_array[@]}
1001 1002 1003 1004

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.