File tree Expand file tree Collapse file tree 2 files changed +57
-0
lines changed
Expand file tree Collapse file tree 2 files changed +57
-0
lines changed Original file line number Diff line number Diff line change @@ -5795,12 +5795,31 @@ impl Interpreter {
57955795 "maximum nesting depth exceeded in command substitution" . to_string ( ) ,
57965796 ) ) ;
57975797 }
5798+ // Command substitution runs in a subshell: snapshot trap state
5799+ // so EXIT traps set inside $() fire and don't leak to parent.
5800+ let saved_traps = self . traps . clone ( ) ;
57985801 let mut stdout = String :: new ( ) ;
57995802 for cmd in commands {
58005803 let cmd_result = self . execute_command ( cmd) . await ?;
58015804 stdout. push_str ( & cmd_result. stdout ) ;
58025805 self . last_exit_code = cmd_result. exit_code ;
58035806 }
5807+ // Fire EXIT trap set inside the command substitution
5808+ if let Some ( trap_cmd) = self . traps . get ( "EXIT" ) . cloned ( )
5809+ && saved_traps. get ( "EXIT" ) != Some ( & trap_cmd)
5810+ && let Ok ( trap_script) = Parser :: with_limits (
5811+ & trap_cmd,
5812+ self . limits . max_ast_depth ,
5813+ self . limits . max_parser_operations ,
5814+ )
5815+ . parse ( )
5816+ && let Ok ( trap_result) =
5817+ self . execute_command_sequence ( & trap_script. commands ) . await
5818+ {
5819+ stdout. push_str ( & trap_result. stdout ) ;
5820+ }
5821+ // Restore parent trap state
5822+ self . traps = saved_traps;
58045823 self . counters . pop_function ( ) ;
58055824 self . subst_generation += 1 ;
58065825 let trimmed = stdout. trim_end_matches ( '\n' ) ;
Original file line number Diff line number Diff line change @@ -185,3 +185,41 @@ echo "$result"
185185### expect
186186(x)(y)(z)
187187### end
188+
189+ ### subst_exit_trap_captured
190+ # EXIT trap output should be captured inside $( ) , not leak to parent
191+ result=" $( trap ' echo TRAPPED' EXIT; echo hello) "
192+ echo " captured=[${result} ]"
193+ ### expect
194+ captured=[hello
195+ TRAPPED]
196+ ### end
197+
198+ ### subst_exit_trap_with_explicit_exit
199+ # EXIT trap fires on explicit exit inside $( )
200+ result=" $( trap ' echo CLEANUP' EXIT; echo data; exit 0) "
201+ echo " captured=[${result} ]"
202+ ### expect
203+ captured=[data
204+ CLEANUP]
205+ ### end
206+
207+ ### subst_exit_trap_no_leak
208+ # Trap output must not leak to parent stdout
209+ out=" $( trap ' echo INSIDE' EXIT; echo body) "
210+ echo " out=[${out} ]"
211+ ### expect
212+ out=[body
213+ INSIDE]
214+ ### end
215+
216+ ### subst_exit_trap_isolation
217+ # EXIT trap in $( ) should not affect parent traps
218+ trap 'echo PARENT' EXIT
219+ result=" $( trap ' echo CHILD' EXIT; echo inner) "
220+ echo " result=[${result} ]"
221+ trap - EXIT
222+ ### expect
223+ result=[inner
224+ CHILD]
225+ ### end
You can’t perform that action at this time.
0 commit comments