6

Take the following script, interrupted by keyboard input Ctrl+C as shown:

$ function a() { echo "Performing"; sleep 10; echo "Performed"; }
$ a
Performing
^C
$ echo "${FUNCNAME[@]}"
a source

If we repeat the SIGINT during function a many times, we eventually end up with this scenario:

...

$ a
Performing
^C
$ a
Performing
^C
$ echo "${FUNCNAME[@]}"
a a a a a a a a a source

To demonstrate the issue, let's have another function, b, that gives an message_error function, also defined below:

$ function message_error() {
>   local MESSAGE="$1"
>
>   # FUNCNAME[1], as [0] is `message_error`, gets the calling function
>   echo "[[ ERROR ]] ${FUNCNAME[1]}: $MESSAGE"
>   echo "Rest of the stack: ${FUNCNAME[@]}"
>}
$ function b() { message_error "Oh no"; }
$ b
[[ ERROR ]] b: Oh no
Rest of the stack: message_error b a a a a a a a a a source

Even though the function is called in the source, a shows up as the calling function. As you can see, all these a functions were exited. However they remain in the FUNCNAME array.

Why is this? How can I remove it after SIGINT fails to remove it? Please note all functions are sourced from a large collection of commonly used functions, ~/.bash_functions, if sourcing the functions affects how they interpret SIGINT.

  • Add a signal handler: trap 'exit' SIGINT – Satō Katsura Jul 15 '16 at 13:11
  • @SatoKatsura The script will then exit on ^C. I don't want to tarnish the ability to interrupt a script mid-run, I simply want the exec trace to be an accurate representation of what's happening. – Nick Bull Jul 15 '16 at 13:13
  • Did you try it? As @PSkocik says, this is only for interactive scripts. – Satō Katsura Jul 15 '16 at 13:14
  • @SatoKatsura Yeah, I added trap 'exit' SIGINT one line below the shebang of ~/.bash_functions. Then I saved the script and opened a new terminal window, and ran function a. The terminal window exited upon pressing ^C. Sorry, forgot to mention that ~/.bash_functions is sourced by ~/.bashrc, which then makes use of the functions within it's script upon terminal start. – Nick Bull Jul 15 '16 at 13:15
  • 1
    TIL: trap ' ' SIGINT is not the same as trap '' SIGINT. How nice. – Satō Katsura Jul 15 '16 at 13:27
1

Since FUNCNAME is a read-only array, there's probably no way to remove items in it. It seems unset FUNCNAME sort of works, yet that also breaks bash's special FUNCNAME behavior.

Prevention of FUNCNAME garbage can be done by using trap:

$ trap : SIGINT
$ function a() {  echo "Performing"; sleep 10; echo "Performed"; } 
$ a
Performing
^C
$ echo %"${FUNCNAME[@]}"%
%%

To disable the above prevention, do trap - SIGINT.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.