LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software
User Name
Password
Linux - Software This forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.

Notices

Reply
 
Search this Thread
Old 09-01-2009, 08:16 AM   #1
notepod
LQ Newbie
 
Registered: Sep 2009
Posts: 11

Rep: Reputation: 1
How to use trap command with a function


Hello

I have a little script that display a menu. I want to catch CTRL-C and simply redraw the menu.

The following simplified script explain the principle:

Code:
#!/bin/sh

trap "menu" SIGINT

function menu () {
    echo [1] - list users
    echo [2] - add user
    echo [3] - delete user
    read answer
}

menu

It works ... only one time. I can press CTRL-C one time, the menu is redrawn, but after that nothing happen when i press CTRL-C again. Does anyone know why ?
I can't transform the menu function into a script because a "trap ./menu.sh SIGINT" start a new process each time and that's not what i want, i want only one process.

Any help would be greatly appreciated !
 
Old 09-01-2009, 08:58 AM   #2
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,528
Blog Entries: 27

Rep: Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176
I can't find reference information about this but I have a vague memory that traps are reset when they are invoked. If that's right you can get the behaviour you want by
Code:
#!/bin/sh

trap "menu" SIGINT

function menu () {
    trap "menu" SIGINT
    echo [1] - list users
    echo [2] - add user
    echo [3] - delete user
    read answer
}

menu
BTW is there a reason why you are using /bin/sh rather than /bin/bash?
 
Old 09-01-2009, 09:12 AM   #3
notepod
LQ Newbie
 
Registered: Sep 2009
Posts: 11

Original Poster
Rep: Reputation: 1
Hi

I tried your code and i have the same behavior. (that is to say, it works only one time)

There's no reason to use /bin/sh rather than /bin/bash but anyway /bin/sh is a simlink to /bin/bash on my system (Debian)

Last edited by notepod; 09-01-2009 at 09:13 AM.
 
Old 09-01-2009, 09:20 AM   #4
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,458

Rep: Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941
Quote:
Originally Posted by notepod View Post
It works ... only one time. I can press CTRL-C one time, the menu is redrawn, but after that nothing happen when i press CTRL-C again. Does anyone know why ?
That's not quite true. It works an indefinite number of times if you just press enter after ^C. This is due to the shell waiting for standard input and expecting an EOF signal. If you put the read answer part outside the function, it works as you expect:
Code:
#!/bin/bash

trap "menu" SIGINT

function menu () {
    echo [1] - list users
    echo [2] - add user
    echo [3] - delete user
}

menu
read answer
 
Old 09-01-2009, 09:25 AM   #5
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,458

Rep: Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941
Quote:
Originally Posted by notepod View Post
There's no reason to use /bin/sh rather than /bin/bash but anyway /bin/sh is a simlink to /bin/bash on my system (Debian)
Just an aside note about this statement: even if /bin/sh is a soft link to /bin/bash the behaviour can be quite different, especially if you use some feature specific to bash. Here is the relevant excerpt from man bash:
Quote:
If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well. When invoked as an interactive login shell, or a non-interactive shell with the --login option, it first attempts to read and execute commands from /etc/profile and ~/.profile, in that order.
The last statement means it does NOT sources ~/.bashrc.
 
Old 09-01-2009, 09:28 AM   #6
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,528
Blog Entries: 27

Rep: Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176
Quote:
Originally Posted by notepod View Post
There's no reason to use /bin/sh rather than /bin/bash but anyway /bin/sh is a simlink to /bin/bash on my system (Debian)
Bash looks at what name it was called with. If it is called as sh then it emulates a POSIX version of sh and many bash features are disabled. See "Invoked with name sh" in the GNU Bash Reference Manual.
 
Old 09-01-2009, 10:13 AM   #7
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,528
Blog Entries: 27

Rep: Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176
Quote:
Originally Posted by colucix View Post
That's not quite true. It works an indefinite number of times if you just press enter after ^C. This is due to the shell waiting for standard input and expecting an EOF signal. If you put the read answer part outside the function, it works as you expect:
Code:
#!/bin/bash

trap "menu" SIGINT

function menu () {
    echo [1] - list users
    echo [2] - add user
    echo [3] - delete user
}

menu
read answer
Hello Colucix

This is intriguing! Can you explain in more detail? Actually, I might have worked it out for myself while writing this post but your comments would e very welcome.

I'd like to fully understand what is happening and may not have it right yet. I have some unsatisfactory trap handling in a (too) big backup script so gaining a full understanding would be helpful.

AIUI, in the original version it goes something like this
  1. trap is set for SIGINT.
  2. function menu is defined.
  3. function menu is called.
  4. function menu calls read (a shell built-in, so saves us some complexity ) and read waits for EOL.
  5. user generates SIGINT.
  6. trap is "sprung" and function menu is called. What happens to the existing menu and read? Are they abandoned and destroyed? Or are they "stacked" to be resumed when the signal handler has finished?
  7. function menu re-writes the menu on stdout and calls read again.
  8. user generates SIGINT. This now has no effect. Why not? Has the trap been unset? Is the trap unset while running the trap handler? We know that the trap was effective the first time, while read was waiting for EOL so what's different this time?
AIUI, in the original version but now pressing Enter to terminate the read between sending SIGINTs it goes something like this
  1. trap is set for SIGINT.
  2. function menu is defined.
  3. function menu is called.
  4. function menu calls read.
  5. user generates SIGINT.
  6. trap is "sprung" and function menu is called.
  7. function menu calls read again.
  8. user presses Enter and read terminates. The only reason I can think of why that does not end the script is that the first read is stacked and is resumed when the signal handler itself finishes executing.
  9. user generates SIGINT again and we start over at step 6. If the user does not generate SIGINT at this time but presses Enter then the first read terminates, the first (not trap handling) menu function terminates and the script terminates. OK? Am I making sense?
Both the above are consistent with my theory that a) trap handling is disabled in the trap handler and b) execution resumes where it left off when the trap handler terminates.

AIUI, in the modified version with read after the in-line call to menu it goes something like this
  1. trap is set for SIGINT.
  2. function menu is defined.
  3. function menu is called and writes the menu.
  4. "main" calls read.
  5. user generates SIGINT.
  6. trap is "sprung" and function menu is called, rewriting the menu.
  7. function menu terminates and execution resumes in the read command, waiting for EOL.
  8. Each time the user generates SIGINT the above two steps are repeated. If the user presses Enter the read terminates and the script terminates (which is exactly what the OP wanted!).
If all this wild theorising is correct then my suggested solution (of re-setting the trap inside the menu function) would not work nicely; the use would end up with multiple read commands waiting for EOL.

Best

Charles
 
Old 09-01-2009, 10:58 AM   #8
notepod
LQ Newbie
 
Registered: Sep 2009
Posts: 11

Original Poster
Rep: Reputation: 1
catkin, I had the exact same questions
Thank you for writing it for me !

I am wondering too what happen to the staled read when the trap handler is called.
 
Old 09-01-2009, 11:09 AM   #9
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,458

Rep: Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941
Hi, Charles!

Your theory is correct. Unfortunately I cannot add anything more (at least anything useful). I can only demonstrate that the trap actually spawns a new process and when this one is terminated the parent is resumed. Try this:
Code:
#!/bin/bash

trap "echo Ctrl+C detected... && menu" SIGINT


function menu () {
    echo [1] - list users
    echo [2] - add user
    echo [3] - delete user
    read answer
    echo $answer
}

menu
Here is my sequence of actions and the corresponding output:
Code:
$ ./test.sh
[1] - list users
[2] - add user
[3] - delete user
^CCtrl+C detected...  ### pressed Ctrl-C here
[1] - list users
[2] - add user
[3] - delete user
^C                    ### pressed Ctrl-C here
                      ### pressed Enter here
Ctrl+C detected...
[1] - list users
[2] - add user
[3] - delete user
1                     ### typed 1 here
1
2                     ### typed 2 here
2
$
As you can see it actually expects two inputs from the user, according to the read statement executed twice. The odd thing is that you can do that indefinitely: if you continue to press Ctrl-C then Enter (let's say 10 times) the accepted input is always and only doubled, then the execution stop. I'm sorry but I cannot explain this behaviour. Furthermore the trap function is not extensively documented, even in the Advanced Bash'. Maybe someone more experienced than me in bash internals can enlighten us.

All the best,
Alex
 
Old 09-01-2009, 11:17 AM   #10
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,458

Rep: Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941
Quote:
Originally Posted by colucix View Post
if you continue to press Ctrl-C then Enter (let's say 10 times) the accepted input is always and only doubled, then the execution stop. I'm sorry but I cannot explain this behaviour.
I quote myself, because after re-reading my post I realized that when you press enter the previous standard input is terminated and the empty line shown in my sequence above is actually an invisible Ctrl-C. The recursion do the rest.

Without recursion you get the expected behaviour:
Code:
$ cat test.sh
#!/bin/bash

trap "echo Ctrl+C detected..." SIGINT

function menu () {
    echo [1] - list users
    echo [2] - add user
    echo [3] - delete user
    read answer
    echo $answer
}

menu
$
$ ./test.sh
[1] - list users
[2] - add user
[3] - delete user
^CCtrl+C detected...
^CCtrl+C detected...
^CCtrl+C detected...
^CCtrl+C detected...
^CCtrl+C detected...
^CCtrl+C detected...
^CCtrl+C detected...
3
3
$
 
Old 09-01-2009, 11:28 AM   #11
notepod
LQ Newbie
 
Registered: Sep 2009
Posts: 11

Original Poster
Rep: Reputation: 1
Quote:
Originally Posted by colucix View Post
I can only demonstrate that the trap actually spawns a new process and when this one is terminated the parent is resumed.
Actually it does not spawn a new process, but you're right when the trap handler is terminated, the "parent" is resumed. This can easely be viewed with this script:
Code:
#!/bin/bash

trap "menu trap_handler" SIGINT

function menu() {
    echo start menu\(\) pid: $$ arg: $1
    read answer
    echo end menu\(\) pid: $$ arg: $1
}

menu test.sh
colucix> I don't see what is odd by pressing Ctrl-C then Enter 10 times, you will enter and exit trap handler ten times, then go back to the first "read", still waiting for you. What's the matter ?
 
Old 09-01-2009, 01:22 PM   #12
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,528
Blog Entries: 27

Rep: Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176Reputation: 1176
Quote:
Originally Posted by notepod View Post
Actually it does not spawn a new process
Agreed -- it's probably implemented using C's setjmp() so happens in the same process. Having done some more experiments, AINUI it goes something like this
  1. trap is set for SIGINT.
  2. function menu is defined.
  3. function menu is called.
  4. function menu calls read which does a blocking line read on stdin.
  5. user generates SIGINT.
  6. trap is "sprung". Bash saves the current contaxt and calls function menu.
  7. function menu re-writes the menu on stdout and calls read again.
  8. according to trap -p SIGINT, the trap is still in effect.
  9. function menu calls read.
  10. Now it gets interesting. If the user generates another SIGINT, it is stored. Any more SIGINTs are discarded. The read is not interrupted, unlike the first read.
  11. user enters a line and read terminates.
  12. now any queued SIGINT is processed and processing continues at step 6.
  13. when there is no queued SIGINT, the original in-line read is resumed.
So now I'm clear except that there are a couple of features not defined in the GNU Bash Reference Manual or the relevant
Open Group Base Specifications Issue 6.

Firstly that SIGINT is not processed while the signal handling function is running the read built-in but is when the same read is being run by the same function when run in-line.

Secondly how many SIGINTs are queued. OK for this to be implementation-dependent.

As the mother of the new wife who slept naked except for a hat said: "It's good to keep some mystery"

In case anyone wants to experient some more, here's my play script
Code:
#!/bin/bash

trap 'echo SIGINT received && my_func as trap handler' SIGINT

function my_func {
	echo "my_func: $*"
	trap -p SIGINT
	read 
	echo "my_func: $* REPLY: $REPLY"
}

my_func 'inline'

Last edited by catkin; 09-01-2009 at 01:23 PM.
 
Old 09-02-2009, 02:14 AM   #13
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,458

Rep: Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941
Quote:
Originally Posted by notepod View Post
colucix> I don't see what is odd by pressing Ctrl-C then Enter 10 times, you will enter and exit trap handler ten times, then go back to the first "read", still waiting for you. What's the matter ?
Indeed I realized it later, as explained in post #10.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
how to send snmp trap & recieve trap in C program minil Programming 3 07-10-2010 09:22 AM
Regarding trap command sharp859 Linux - Newbie 1 05-11-2009 07:35 PM
what does the command do: trap ' ' 1 2 ? manohare Linux - General 2 03-02-2009 05:51 AM
Kernel trap (Fatal trap 12) m!k@EL *BSD 4 09-05-2007 11:58 PM
trap command for c? onnyloh Programming 3 09-13-2004 04:06 AM


All times are GMT -5. The time now is 01:09 PM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration