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 |
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
Are you new to LinuxQuestions.org? Visit the following links:
Site Howto |
Site FAQ |
Sitemap |
Register Now
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
|
 |
09-01-2009, 08:16 AM
|
#1
|
LQ Newbie
Registered: Sep 2009
Posts: 11
Rep:
|
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 !
|
|
|
09-01-2009, 08:58 AM
|
#2
|
LQ 5k Club
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
|
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?
|
|
|
09-01-2009, 09:12 AM
|
#3
|
LQ Newbie
Registered: Sep 2009
Posts: 11
Original Poster
Rep:
|
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.
|
|
|
09-01-2009, 09:20 AM
|
#4
|
LQ Guru
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509
|
Quote:
Originally Posted by notepod
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
|
|
|
09-01-2009, 09:25 AM
|
#5
|
LQ Guru
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509
|
Quote:
Originally Posted by notepod
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.
|
|
|
09-01-2009, 09:28 AM
|
#6
|
LQ 5k Club
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
|
Quote:
Originally Posted by notepod
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.
|
|
|
09-01-2009, 10:13 AM
|
#7
|
LQ 5k Club
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
|
Quote:
Originally Posted by colucix
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 - trap is set for SIGINT.
- function menu is defined.
- function menu is called.
- function menu calls read (a shell built-in, so saves us some complexity
) and read waits for EOL.
- user generates SIGINT.
- 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?
- function menu re-writes the menu on stdout and calls read again.
- 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 - trap is set for SIGINT.
- function menu is defined.
- function menu is called.
- function menu calls read.
- user generates SIGINT.
- trap is "sprung" and function menu is called.
- function menu calls read again.
- 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.
- 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 - trap is set for SIGINT.
- function menu is defined.
- function menu is called and writes the menu.
- "main" calls read.
- user generates SIGINT.
- trap is "sprung" and function menu is called, rewriting the menu.
- function menu terminates and execution resumes in the read command, waiting for EOL.
- 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
|
|
|
09-01-2009, 10:58 AM
|
#8
|
LQ Newbie
Registered: Sep 2009
Posts: 11
Original Poster
Rep:
|
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.
|
|
|
09-01-2009, 11:09 AM
|
#9
|
LQ Guru
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509
|
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
|
|
|
09-01-2009, 11:17 AM
|
#10
|
LQ Guru
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509
|
Quote:
Originally Posted by colucix
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
$
|
|
|
09-01-2009, 11:28 AM
|
#11
|
LQ Newbie
Registered: Sep 2009
Posts: 11
Original Poster
Rep:
|
Quote:
Originally Posted by colucix
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 ?
|
|
|
09-01-2009, 01:22 PM
|
#12
|
LQ 5k Club
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
|
Quote:
Originally Posted by notepod
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 - trap is set for SIGINT.
- function menu is defined.
- function menu is called.
- function menu calls read which does a blocking line read on stdin.
- user generates SIGINT.
- trap is "sprung". Bash saves the current contaxt and calls function menu.
- function menu re-writes the menu on stdout and calls read again.
- according to trap -p SIGINT, the trap is still in effect.
- function menu calls read.
- 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.
- user enters a line and read terminates.
- now any queued SIGINT is processed and processing continues at step 6.
- 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.
|
|
|
09-02-2009, 02:14 AM
|
#13
|
LQ Guru
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509
|
Quote:
Originally Posted by notepod
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. 
|
|
|
All times are GMT -5. The time now is 04:08 PM.
|
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.
|
Latest Threads
LQ News
|
|