Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Bash continue [n] ignored in piped to while read loops. for loop behaves as expected

In a script I’m writing, I try to exit out of the second nested while loop, and the continue statement appears to be simply ignored, and I’m unsure of exactly why.

From the manual:

continue [n]:
    Resume  the  next  iteration of the enclosing for, while, until, or select loop.  If n is specified,
    resume at the nth enclosing loop.  n must be ≥ 1.  If n is greater  than  the  number  of  enclosing
    loops,  the last enclosing loop (the ``top-level'' loop) is resumed.  The return value is 0 unless n
    is not greater than or equal to 1.

A minimal example that illustrates my problem (ex1.sh):

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#!/bin/bash

printf "%d\n" {1..3} | while read i; do
  printf "%d\n" {1..3} | while read j; do
    sum=$(expr $i + $j)
    echo "$i + $j = $sum"
    [ $sum -ge 5 ] && echo continue && continue 2
  done
  echo 'should only see this for first cycle of i (2+3 >= 5)'
done

Output of ex1.sh:

1 + 1 = 2
1 + 2 = 3
1 + 3 = 4
should only see this for first cycle of i (2+3 >= 5)
2 + 1 = 3
2 + 2 = 4
2 + 3 = 5
continue
should only see this for first cycle of i (2+3 >= 5)
3 + 1 = 4
3 + 2 = 5
continue
3 + 3 = 6
continue
should only see this for first cycle of i (2+3 >= 5)

Why is the continue 2 ignored? It clearly echos out "continue", and the fact that continue runs is confirmed by running a trace with set -x

The use of printf is obviously silly ex1.sh, but reproduces the issue I’m having with getting continue to work in a while read x... loop.

A for loop works as expected (ex2.sh):

#!/bin/bash

for i in {1..3}; do
  for j in {1..3}; do
    sum=$(expr $i + $j)
    echo "$i + $j = $sum"
    [ $sum -ge 5 ] && echo continue && continue 2
  done
  echo 'should only see this for first cycle of i (2+3 >= 5)'
done

Output of ex2.sh:

1 + 1 = 2
1 + 2 = 3
1 + 3 = 4
should only see this for first cycle of i (2+3 >= 5)
2 + 1 = 3
2 + 2 = 4
2 + 3 = 5
continue
3 + 1 = 4
3 + 2 = 5
continue

So, why does ex1.sh seem to ignore the continue 2 builtin, whereas ex2.sh behaves as expected?

Fixed the first example myself thanks to M. Nejat Aydin:

#!/bin/bash

while read i; do
  while read j; do
    sum=$(expr $i + $j)
    echo "$i + $j = $sum"
    [ $sum -ge 5 ] && echo continue && continue 2
  done < <(printf "%d\n" {1..3})
  echo 'should only see this for first cycle of i (2+3 >= 5)'
done < <(printf "%d\n" {1..3})

The issue is that pipelines create subshells, so my continue statement was behaving as expected according to the manual, continuing at the outermost loop that subshell was aware of.

>Solution :

Your while loops are not nested. They’re running in different subshells. continue cannot continue from the parent shell. A pipe (|) creates a subshell. On the other hand, the version below should work as expected because both whiles are running in the same shell:

while read i; do
  while read j; do
    sum=$(expr $i + $j)
    echo "$i + $j = $sum"
    [ $sum -ge 5 ] && echo continue && continue 2
  done < <(printf "%d\n" {1..3})
  echo 'should only see this for first cycle of i (2+3 >= 5)'
done < <(printf "%d\n" {1..3})
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading