SIGTSTP (Ctrl-Z) stops only the subshell, but the parent shell keeps running - interactive bash shell

37 Views Asked by At

in an interacive bash shell, i want to call a function,
which starts a subshell, to run some bash code

f() { echo a; ( sleep 5; ); echo z $?; }; f

i need the subshell to catch exit 1 from the bash code,
so my interacive bash shell keeps running.
see also Is there a TRY CATCH command in Bash

the code in the subshell is a long-running process, for example sleep 5

problem: when i call the function and send SIGTSTP (Ctrl-Z),
then only the subshell is stopped, and the main function keeps running

expected: SIGTSTP (Ctrl-Z) should stop both the main function and the subshell,
and SIGCONT (fg) should continue running both

workaround: build bash with --disable-job-control,
because set +m does not work, and stty susp undef is too limiting

$ f() { echo a; ( sleep 5; ); echo z $?; }; f
a
^Z
[1]+  Stopped                 ( sleep 5 )
z 148

$ fg
( sleep 5 )

$

the return code 148 means 128 + 20, and 20 == SIGTSTP
see also Exit status ($?) of 148 upon Ctrl+Z

with wait -f %1 in the main function,
the main function hangs, and i have to interrupt (Ctrl-C)

%1 works here, because there is only one job

$ f() { echo a; ( sleep 5; ); wait -f %1; echo z $?; }; f
a
^Z
[1]+  Stopped                 ( sleep 5 )
bash: warning: wait_for_job: job 1 is stopped
^Z^Z^Z^Z^Z^Z^C

$

with trap "" SIGTSTP in the subshell, the SIGTSTP (Ctrl-Z) is ignored

$ f(){ echo a; ( trap "" SIGTSTP; sleep 5; ); echo z $?; }; f
a
^Z^Z^Z^Z^Z^Z^Zz 0

$ 

with trap handle_tstp SIGTSTP in the subshell,
the interactive shell hangs on SIGTSTP (Ctrl-Z)

$ f(){ echo a; ( s() { echo s; }; trap s SIGTSTP; sleep 5; ); echo z $?; }; f
a
^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^C^C^C^C^C^C^C^C^C^C^C^C^C

trap handle_tstp SIGTSTP in the main function has no effect

f(){ s() { echo s; }; trap s SIGTSTP; echo a; ( sleep 5; ); echo z $?; }; f 

kill -SIGTSTP $$ in the main function has no effect,
so the main function cannot stop itself

f(){ echo a; ( sleep 5; ); kill -SIGTSTP $$; echo z $?; }; f

$$ versus $BASHPID, parent shell versus subshell

$ f(){ echo par $$ $BASHPID; ( echo sub $$ $BASHPID; ) }; f
par 613627 613627
sub 613627 613652

more tricks i tried... but the SIGTSTP always stops only a part, and the other part keeps running in the background. i also tried to trap the SIGTSTP to manually kill -SIGTSTP $subshell_pid but the trap fails to catch the SIGTSTP

with this, SIGTSTP is ignored

r() { echo $$.b; while read -r L; do echo "$$.b $L"; done <$1; echo $$.d; }; f() { echo $$.a; r <( for ((i=0;i<5;i++)); do echo $BASHPID.c.$i; sleep 1; done; ); echo $$.z $?; }; f

with these, SIGTSTP stops only the foreground process

r() { echo $$.b; cat $1 | while read -r L; do echo "$$.b $L"; done; echo $$.d; }; f() { echo $$.a; r <( for ((i=0;i<5;i++)); do echo $BASHPID.c.$i; sleep 1; done; ); echo $$.z $?; }; f

r() { echo $$.b; cat $1; echo $$.d; }; f() { echo $$.a; r <( for ((i=0;i<5;i++)); do echo $BASHPID.c.$i; sleep 1; done; ); echo $$.z $?; }; f

r() { echo $$.b; t() { echo trap; }; trap t SIGTSTP; cat $1 | while read -r L; do echo "$$.b $L"; done; echo $$.d; }; f() { echo $$.a; r <( for ((i=0;i<5;i++)); do echo $BASHPID.c.$i; sleep 1; done; ); echo $$.z $?; }; f

see also: Why does a subshell of an interactive shell run as an interactive shell?

A subshell inherits all the characteristics of the parent

0

There are 0 best solutions below