Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: Loops and pipes
- X-seq: zsh-workers 55114
- From: Philippe Altherr <philippe.altherr@xxxxxxxxx>
- To: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- Cc: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: Re: Loops and pipes
- Date: Mon, 24 Aug 2026 00:40:53 +0200
- Arc-authentication-results: i=1; mx.google.com; arc=none
- Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=BLvEjFBz0YNxcvAWU/VdDgK82I6hZo5+kQeWy+A6W6g=; fh=vuDAGjptCPc/P1/HEZ3j98yPJEW1XjuEoEAmmwDS/+U=; b=bWVWMLWP1HQa3zrB5qh53ReZq27V1KCXNP/pNNPuKegAGftjNX/jUIDiYwN/CuiZZW 1i7prcUHu9HV0/5h0O5Erizj3jXZNGMJ5/R7K8bklYebic1xpUErXJRdsm0aBdDGp4qX 9VkQ+P9FRfeHWzgpd5etfFt2lhrZsXo0P841dk7qygGxdmpQFFHUYvu1mIfYFp7yYBXq Ghj5oTCnN1NaE6X2CFv4acjnu6WVGWdGL+nsYznE+0WA7HT3S2CutJTyl28XsGQsg1dx 3zXkbfdxNiZZOzHLWM8Wdq2wdR1OuiqBmbI9iyCh9PggyVhT2Ckk22z12ZznxZdHKmST KO2g==; darn=zsh.org
- Arc-seal: i=1; a=rsa-sha256; t=1787524865; cv=none; d=google.com; s=arc-20260327; b=LHU1B6uEOwzMLjOAB/hT3P4PLajuuOiPF2G6ApxJPvuvkg5CzcpQ7Xt0ByrdCEcZ9q rIUCFPef+MmDI9lGL9ETzXYYsWPOBDC+UrL2sKdo2qzlptKQqVfXteFcpfh+on+BSkNp iH4WM2HBigRuOzzZRSVMWT8gq1Gtx0PqJPm8duG8HVoN9uzVHYa+PCB+MqgK4jzaQWzG HwfQ59al0107cZvhCMZ+UlYAxHtsav6IraQZrkSE8Uv+8togzh/fQTAWtXQFC3GS6z97 AhoH2hz18XLkVGpCWUdoffNbhdfAwCTUvQMtn88ijdPo2xN+KDULelP1eSLcYzAQX9IV ZSyw==
- Archived-at: <https://zsh.org/workers/55114>
- In-reply-to: <CAGdYchtnz_ti5BSmyUFGQr2y3UYE5OCAi6PE9+nsh==rHbf4CQ@mail.gmail.com>
- List-id: <zsh-workers.zsh.org>
- References: <CAH+w=7aqY8wsSSczg_79gPzeA2zfQA=hLhKhXXnLLgNKK_0GKQ@mail.gmail.com> <CAH+w=7bj2yiU_o5jv9Q=J5FHxoCX9j3fn5OKTCBQhvk2qsr7pw@mail.gmail.com> <CAGdYchsAhEE5jRd3o9Kc=KBL4qdCbGs4N3n4bHH1wDnoaQrhEQ@mail.gmail.com> <CAH+w=7Zh8+dKT7hwyQquZgr=DYzYXKrLs40h_0WOyam+zAT-7Q@mail.gmail.com> <CAGdYchtnz_ti5BSmyUFGQr2y3UYE5OCAi6PE9+nsh==rHbf4CQ@mail.gmail.com>
Here is an alternate version of the second patch that emits a warning rather than an error when breaking/continuing beyond the outermost loop in the same subshell. This version also prevents "continue" from continuing the outermost loop when continuing beyond it. The following script will only print A (and a warning), while it currently prints A, B, and C.
for X in A B C; do
echo $X
continue 2
done
Philippe
diff --git a/Src/builtin.c b/Src/builtin.c
index 4d47227ab..0d1a0058f 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5824,13 +5824,19 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
zerrnam(name, "argument is not positive: %d", num);
return 1;
}
- if (!loops) { /* break/continue only permitted in loops */
+ if (!loops) {
zerrnam(name, ancestor_loops
? "not in same subshell as first enclosing loop"
: "not in for, while, until, select, or repeat loop");
return 1;
}
- contflag = func == BIN_CONTINUE;
+ if (num > loops) {
+ zwarnnam(name, ancestor_loops
+ ? "not in same subshell as first %d enclosing loops"
+ : "not in %d for, while, until, select, or repeat loops",
+ num);
+ }
+ contflag = func == BIN_CONTINUE && num <= loops;
breaks = minimum(num, loops);
break;
case BIN_RETURN:
diff --git a/Test/A07control.ztst b/Test/A07control.ztst
index 6054a363d..33281929e 100644
--- a/Test/A07control.ztst
+++ b/Test/A07control.ztst
@@ -37,16 +37,138 @@
1:continue outside loop
?fn:continue:1: not in for, while, until, select, or repeat loop
- for x in a b c; do
- ( echo $x:before; continue 2>&1; echo $x:after )
- done
-1:continue in subshell
->a:before
->(eval):continue:2: not in same subshell as first enclosing loop
->b:before
->(eval):continue:2: not in same subshell as first enclosing loop
->c:before
->(eval):continue:2: not in same subshell as first enclosing loop
+ local cmd script=()
+ for cmd in "break 2" "break 3" "break 4" "continue 2" "continue 3" "continue 4"; do
+ script+='
+ echo "Testing \"'$cmd'\":"
+ for X in A B; do
+ for Y in p q; do
+ for Z in 1 2; do
+ echo "'$cmd': $X$Y$Z before"
+ '$cmd' 2>&1
+ echo "'$cmd': $X$Y$Z after"
+ done
+ echo "'$cmd': $X$Y- after"
+ done
+ echo "'$cmd': $X-- after"
+ done
+ echo "'$cmd': --- after"
+ '
+ done
+ $ZTST_testdir/../Src/zsh -fc "$script"
+0:break/continue beneath, at, and beyond the outermost loop
+>Testing "break 2":
+>break 2: Ap1 before
+>break 2: A-- after
+>break 2: Bp1 before
+>break 2: B-- after
+>break 2: --- after
+>Testing "break 3":
+>break 3: Ap1 before
+>break 3: --- after
+>Testing "break 4":
+>break 4: Ap1 before
+>zsh:break:35: not in 4 for, while, until, select, or repeat loops
+>break 4: --- after
+>Testing "continue 2":
+>continue 2: Ap1 before
+>continue 2: Aq1 before
+>continue 2: A-- after
+>continue 2: Bp1 before
+>continue 2: Bq1 before
+>continue 2: B-- after
+>continue 2: --- after
+>Testing "continue 3":
+>continue 3: Ap1 before
+>continue 3: Bp1 before
+>continue 3: --- after
+>Testing "continue 4":
+>continue 4: Ap1 before
+>zsh:continue:77: not in 4 for, while, until, select, or repeat loops
+>continue 4: --- after
+
+ local cmd X
+ for cmd in "break" "continue"; do
+ echo "Testing \"$cmd\":"
+ for X in A B; do
+ (
+ echo "$cmd: $X before"
+ $cmd 2>&1;
+ echo "$cmd: $X after1"
+ )
+ echo "$cmd: $X after2"
+ done
+ done
+0:break/continue in subshell
+>Testing "break":
+>break: A before
+>(eval):break:7: not in same subshell as first enclosing loop
+>break: A after2
+>break: B before
+>(eval):break:7: not in same subshell as first enclosing loop
+>break: B after2
+>Testing "continue":
+>continue: A before
+>(eval):continue:7: not in same subshell as first enclosing loop
+>continue: A after2
+>continue: B before
+>(eval):continue:7: not in same subshell as first enclosing loop
+>continue: B after2
+
+ local cmd X
+ for cmd in "break 1" "break 2" "break 3" "continue 1" "continue 2" "continue 3"; do
+ echo "Testing \"$cmd\":"
+ for X in A; do
+ (
+ for Y in p q; do
+ for Z in 1 2; do
+ echo "$cmd: $X$Y$Z before"
+ eval $cmd 2>&1
+ echo "$cmd: $X$Y$Z after"
+ done
+ echo "$cmd: $X$Y- after"
+ done
+ echo "$cmd: $X-- after1"
+ )
+ echo "$cmd: $X-- after2"
+ done
+ done
+0:break/continue beneath, at, and beyond the outermost same-subshell loop
+>Testing "break 1":
+>break 1: Ap1 before
+>break 1: Ap- after
+>break 1: Aq1 before
+>break 1: Aq- after
+>break 1: A-- after1
+>break 1: A-- after2
+>Testing "break 2":
+>break 2: Ap1 before
+>break 2: A-- after1
+>break 2: A-- after2
+>Testing "break 3":
+>break 3: Ap1 before
+>(eval):break:1: not in same subshell as first 3 enclosing loops
+>break 3: A-- after1
+>break 3: A-- after2
+>Testing "continue 1":
+>continue 1: Ap1 before
+>continue 1: Ap2 before
+>continue 1: Ap- after
+>continue 1: Aq1 before
+>continue 1: Aq2 before
+>continue 1: Aq- after
+>continue 1: A-- after1
+>continue 1: A-- after2
+>Testing "continue 2":
+>continue 2: Ap1 before
+>continue 2: Aq1 before
+>continue 2: A-- after1
+>continue 2: A-- after2
+>Testing "continue 3":
+>continue 3: Ap1 before
+>(eval):continue:1: not in same subshell as first 3 enclosing loops
+>continue 3: A-- after1
+>continue 3: A-- after2
for outer in 0 1 2 3; do
print outer $outer
Messages sorted by:
Reverse Date,
Date,
Thread,
Author