Configuration Information [Automatically generated, do not change]: Machine: x86_64 OS: Linux-gnu Compiler: gcc Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64' -DCONF_OSTYPE='Linux-gnu' -DCONF_MACHTYPE='x86_64-redhat-linux-gnu' -DCONF_VENDOR='redhat' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash' -DSHELL -DHAVE_CONFIG_H -I. -I. -I./include -I./lib -D_GNU_SOURCE -DRECYCLES_PIDS -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -fwrapv uname output: Linux (hostname removed) 2.6.32-504.8.1.el6.x86_64 #1 SMP Fri Dec 19 12:09:25 EST 2014 x86_64 x86_64 x86_64 GNU/Linux Machine Type: x86_64-redhat-linux-gnu
Bash Version: 4.1 Patch Level: 2 Release Status: release Description: I ran into this issue while experimenting with gnu make and .ONESHELL. I've boiled the issue down to the following simple example: $ newline=$'\n' $ bash -e -c "trap 'echo handler' INT ERR;/bin/true; sleep 2; ${newline}/bin/false" It will successfully trap if I do the following: * hit ^C during the sleep * use "false" instead of "/bin/false" because that'll use a builtin * trap on EXIT instead * remove the newline * add another command after "/bin/false" * add another command before "/bin/false" without a newline after it If I replace "/bin/false" with a sleep then hit ^C then the INT trap also won't execute. I traced this down to builtins/evalstring.c inside parse_and_execute (this is from the latest "develop" version of the file, not from the version I tested with): #if defined (ONESHOT) /* * IF * we were invoked as `bash -c' (startup_state == 2) AND * parse_and_execute has not been called recursively AND * we're not running a trap AND * we have parsed the full command (string == '\0') AND * we're not going to run the exit trap AND * we have a simple command without redirections AND * the command is not being timed AND * the command's return status is not being inverted * THEN * tell the execution code that we don't need to fork */ if (should_suppress_fork (command)) { command->flags |= CMD_NO_FORK; command->value.Simple->flags |= CMD_NO_FORK; } else if (command->type == cm_connection) optimize_fork (command); #endif /* ONESHOT */ ... and it appears this is the culprit. At first I was under the mistaken assumption "we're not running a trap" meant "a trap is set that could be triggered" when in reality it means what it says -- a trap is currently running. Fix: Two possible fixes come to mind: 1) Have the above code loop over the possible signals calling "signal_is_trapped" 2) Alter "change_signal" as follows: /* mask of currently trapped signals */ unsigned long long current_traps; /*...*/ /* If SIG has a string assigned to it, get rid of it. Then give it VALUE. */ static void change_signal (sig, value) int sig; char *value; { if ((sigmodes[sig] & SIG_INPROGRESS) == 0) free_trap_command (sig); trap_list[sig] = value; sigmodes[sig] |= SIG_TRAPPED; if (value == (char *)IGNORE_SIG) sigmodes[sig] |= SIG_IGNORED; else sigmodes[sig] &= ~SIG_IGNORED; if (sigmodes[sig] & SIG_INPROGRESS) sigmodes[sig] |= SIG_CHANGED; + + if( sigmodes[sig] & SIG_TRAPPED ) { + current_traps |= 1 << sig; + } else { + current_traps &= ~(1 << sig); + } } ... then alter the block quoted above in parse_and_execute to check the value of "current_traps". -brian