4

I'm still pretty new to bash scripting, and I'm having a hard time figuring out why this simple trap is not working as expected.

Goal - create an optional waiting period that can be skipped by pressing CTRL+C.

Expected result of pressing CTRL+C - immediately echo "No time for napping!" and exit.

Actual result of pressing CTRL+C - immediately echo "naptime over." and exit.

#!/bin/bash

nonap() {
    echo "No time for napping!"
    exit
}

trap nonap INT

echo "Sleeping for 5 seconds, hit ctrl-c to proceed now."
sleep 5 
echo "Naptime over."

Why is my trap function not invoked?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • 1
    This works fine for me. What's your environment? – Adam May 17 '12 at 05:05
  • 1
    I just tried it (on ancient Linux with bash 3.2.25) and saved your code in `trap.sh`, ran `bash trap.sh`, and got: `Sleeping for 5 seconds, hit ctrl-c to proceed now.` followed by `No time for napping!` when I interrupted, as you expected. – Jonathan Leffler May 17 '12 at 05:05
  • Thanks for the feedback. At least I know it *should* work as expected. I'm using the latest version of Tinycore Linux with GNU bash, version 4.0.33(1)-release (i686-pc-linux-gnu). Upon opening a new terminal, declare -f nonap and trap both return no output. After running this script and getting the "Naptime over." output, `trap` returns `trap -- 'nonap' SIGINT` and `declare -f nonap` returns the function as defined in my script. – Craig Cummings May 17 '12 at 05:13
  • 1
    How are you running this script, then? Using `source` or `.` to read it? Ah, yes; you must be. I just tried that, and the interrupt while sourcing gave me `Naptime over.`; typing another interrupt though gave me `No time for napping!` and the shell exited. The second time it behaved as expected; I'm not sure what's up with the interrupt while dotting the script. That is unexpected behaviour. – Jonathan Leffler May 17 '12 at 05:20
  • Why did you want to source or dot this? Why not just use it as a plain old script? – Jonathan Leffler May 17 '12 at 05:25
  • No reason to source...I was just using that to run it while testing. I guess I've never run into any anomalies like this when using . before, but I'm still a newb too. I am seeing the same results you are, and it works as expected on the first interrupt if I run it with bash. Thanks for figuring that out. It was driving me crazy. – Craig Cummings May 17 '12 at 05:31

1 Answers1

4

I just tried it (on an ancient RHEL Linux with bash 3.2.25) and saved your code in trap.sh, ran bash trap.sh, and got:

 Sleeping for 5 seconds, hit ctrl-c to proceed now.

followed by:

No time for napping!

when I interrupted, as you expected. When I let it run without interrupting, I got the expected message:

Naptime over.

You then commented:

At least I know it should work as expected. I'm using the latest version of Tinycore Linux with GNU bash, version 4.0.33(1)-release (i686-pc-linux-gnu). Upon opening a new terminal, declare -f nonap and trap both return no output. After running this script and getting the "Naptime over." output, trap returns trap -- 'nonap' SIGINT and declare -f nonap returns the function as defined in my script.

To which I responded:

How are you running this script, then? Using source or . to read it? Ah, yes; you must be. I just tried that, and the interrupt while sourcing gave me Naptime over.; typing another interrupt though gave me No time for napping! and the shell exited. The second time it behaved as expected; I'm not sure what's up with the interrupt while dotting the script. That is unexpected behaviour.

Why did you want to source or dot this? Why not just use it as a plain old script?

No reason to source...I was just using that to run it while testing. I guess I've never run into any anomalies like this when using . before, but I'm still a newb too. I am seeing the same results you are, and it works as expected on the first interrupt if I run it with bash.

Well, there's a "Doctor, Doctor, it hurts when I hit my head against the wall" component to the following advice, but there's also basic pragmatism in there too.

You use source (in C shell or bash) or . (in Bourne, Korn, POSIX shells or bash) to have the script affect the environment of the invoking shell, rather than running as a sub-shell. The giveaway to solving the problem (albeit largely by fluke) was when you reported that after running the script, you had the function defined; that can't happen unless you were using source. In this case, it is fairly clear that you do not want the trap set in the calling shell. When I ran it (from a ksh with prompt Toru JL:), I got:

Toru JL: bash
bash-3.2$ trap
bash-3.2$ source trap.sh
Sleeping for 5 seconds, hit ctrl-c to proceed now.

Naptime over.
bash-3.2$ trap
trap -- 'nonap' INT
bash-3.2$ No time for napping!
Toru JL: 

The 'No time for napping!' message appeared when I hit the interrupt key again, and it terminated the bash I'd run. If you continue to use it with source, you would want to add trap INT to the end of the script, and you might also want to undefine the the function.

However, you are much better off isolating it all in a shell and running it as a sub-process, I think.

But...your finding that this sort of thing plays funny games when the script is sourced is interesting. It's a minor anomaly in the behaviour of bash. I'm not sure it rises to the level of 'bug'; I'd have to read a lot of manual rather carefully (probably several times) and consult with other knowledgeable people before claiming 'bug'.

I'm not sure it will be any consolation, but I tried ksh on your script with . and it worked as we'd both expect:

Toru JL: ksh
$ . trap.sh
Sleeping for 5 seconds, hit ctrl-c to proceed now.
No time for napping!
Toru JL: 
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks again for the thoughtful responses. I was reviewing source/. after you figured out that was causing the problem. From what I could gather, sounds like you're dead on. I'm not convinced it's a "bug" either...maybe just a nuanced reason that I'm not fully grasping. – Craig Cummings May 17 '12 at 15:46
  • I will be running all my scripts WITHOUT source from now on unless I have a good reason to do so. So, you solved my immediate problem and taught me a good lesson about invoking scripts in the process. Thanks again! – Craig Cummings May 17 '12 at 15:57