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