Evan
Evan

Reputation: 2301

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):

#!/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.

Upvotes: 2

Views: 362

Answers (1)

M. Nejat Aydin
M. Nejat Aydin

Reputation: 10123

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})

Upvotes: 3

Related Questions