File tree Expand file tree Collapse file tree 2 files changed +76
-2
lines changed
Expand file tree Collapse file tree 2 files changed +76
-2
lines changed Original file line number Diff line number Diff line change @@ -1142,7 +1142,8 @@ impl Interpreter {
11421142 // Run ERR trap on non-zero exit (unless in conditional chain)
11431143 if exit_code != 0 {
11441144 let suppressed = matches ! ( command, Command :: List ( _) )
1145- || matches ! ( command, Command :: Pipeline ( p) if p. negated) ;
1145+ || matches ! ( command, Command :: Pipeline ( p) if p. negated)
1146+ || result. errexit_suppressed ;
11461147 if !suppressed {
11471148 self . run_err_trap ( & mut stdout, & mut stderr) . await ;
11481149 }
@@ -1151,9 +1152,12 @@ impl Interpreter {
11511152 // errexit (set -e): stop on non-zero exit for top-level simple commands.
11521153 // List commands handle errexit internally (with && / || chain awareness).
11531154 // Negated pipelines (! cmd) explicitly handle the exit code.
1155+ // Compound commands (for/while/until) propagate errexit_suppressed when
1156+ // their body ends with an AND-OR chain failure.
11541157 if self . is_errexit_enabled ( ) && exit_code != 0 {
11551158 let suppressed = matches ! ( command, Command :: List ( _) )
1156- || matches ! ( command, Command :: Pipeline ( p) if p. negated) ;
1159+ || matches ! ( command, Command :: Pipeline ( p) if p. negated)
1160+ || result. errexit_suppressed ;
11571161 if !suppressed {
11581162 break ;
11591163 }
Original file line number Diff line number Diff line change 1+ //! Test for issue #873: set -e incorrectly triggers on compound commands
2+ //! whose body ends with && chain failure.
3+
4+ use bashkit:: Bash ;
5+
6+ #[ tokio:: test]
7+ async fn set_e_for_loop_and_chain_no_exit ( ) {
8+ let mut bash = Bash :: new ( ) ;
9+ let result = bash
10+ . exec (
11+ r#"
12+ set -euo pipefail
13+ result=""
14+ for src in yes no; do
15+ [[ "${src}" == "yes" ]] && result="${src}"
16+ done
17+ echo "result: ${result}"
18+ "# ,
19+ )
20+ . await
21+ . unwrap ( ) ;
22+ assert ! (
23+ result. stdout. contains( "result: yes" ) ,
24+ "expected 'result: yes', got: {}" ,
25+ result. stdout
26+ ) ;
27+ }
28+
29+ #[ tokio:: test]
30+ async fn set_e_while_loop_and_chain_no_exit ( ) {
31+ let mut bash = Bash :: new ( ) ;
32+ let result = bash
33+ . exec (
34+ r#"
35+ set -e
36+ i=0
37+ while [[ $i -lt 3 ]]; do
38+ [[ $i -eq 1 ]] && echo "found one"
39+ ((i++)) || true
40+ done
41+ echo "done"
42+ "# ,
43+ )
44+ . await
45+ . unwrap ( ) ;
46+ assert ! (
47+ result. stdout. contains( "found one" ) ,
48+ "stdout: {}" ,
49+ result. stdout
50+ ) ;
51+ assert ! ( result. stdout. contains( "done" ) , "stdout: {}" , result. stdout) ;
52+ }
53+
54+ #[ tokio:: test]
55+ async fn set_e_plain_failure_in_loop_still_exits ( ) {
56+ let mut bash = Bash :: new ( ) ;
57+ let result = bash
58+ . exec (
59+ r#"
60+ set -e
61+ for x in a b; do
62+ false
63+ done
64+ echo "SHOULD NOT APPEAR"
65+ "# ,
66+ )
67+ . await
68+ . unwrap ( ) ;
69+ assert ! ( !result. stdout. contains( "SHOULD NOT APPEAR" ) ) ;
70+ }
You can’t perform that action at this time.
0 commit comments