1

Is there a way to terminate a shell function non-interactively without killing the shell that's running it?

I know that the shell can be told how to respond to a signal (e.g. USR1), but I can't figure out how the signal handler would terminate the function.

If necessary you may assume that the function to be terminate has been written in such a way that it is "terminable" (i.e. by declaring some suitable options).

(My immediate interest is in how to do this for zsh, but I'm also interested in knowing how to do it for bash and for /bin/sh.)

EDIT: In response to Rob Watt's suggestion:

% donothing () { echo $$; sleep 1000000 }
% donothing
47139

If at this point I hit Ctrl-C at the same terminal that is running the shell, then the function donothing does indeed terminate, and I get the command prompt back. But if instead, from a different shell session, I run

% kill -s INT 47139

...the donothing function does not terminate.

kjo
  • 33,683
  • 52
  • 148
  • 265
  • 1
    Do you mean something like control-c? – Rob Watts Apr 03 '13 at 18:07
  • google e.g. "bash signal handling" and first match descripes signal handling for the bash-shell with a lot of examples using `trap` – Fredrik Pihl Apr 03 '13 at 18:23
  • @RobWatts: I've edited my post to account for your suggestion. – kjo Apr 03 '13 at 19:56
  • @FredrikPihl: I don't see an answer to my question among the examples in that page. They all show how to terminate a script, not how to terminate a function. – kjo Apr 03 '13 at 20:01
  • Perhaps it would help if you said how you are planning to use it? You might be able to accomplish your goal in a different way. – Rob Watts Apr 03 '13 at 20:14

1 Answers1

1

Maybe I'm not fully understand what you want, but maybe something like this?

trap "stopme=1" 2

function longcycle() {
    last=$1
    for i in 1 2 3 4 5 
    do
        [ ! -z "$stopme" ] && return
        echo $i
        sleep 1
    done
}

stopme=""
echo "Start 1st cycle"
longcycle
echo "1st cycle end"

echo "2nd cycle"
stopme=""
longcycle
echo "2nd cycle end"

The above is for bash. Run it, and try press CTRL-C.

Or for not interactively, Save the above as for example my_command, then try:

$ ./my_command &  #into background
$ kill -2 $! #send CTRL-C to the bg process

EDIT:

Solution for your sleep example in the bash:

$ donothing() { trap '[[ $mypid ]] && trap - 2 && kill $mypid' 0 2; sleep 1000000 & mypid=$!;wait; }
$ donothing

when you send a signal from another terminal will terminate it. Remeber, signal '0' je "normal end of the process". Semantic name: 0=EXIT, 2=INT... etc.

and remeber too, than signals are sending to processes not to the functions. In your example, the process is the current (interactive shell), so must use the wait trick to get something interrupt-able... Not a nice solution - but the only way when want interrupt something what is running in interactive shell (not a forked one) from the another terminal...

clt60
  • 62,119
  • 17
  • 107
  • 194
  • why can't I replicate the effect of hitting `Ctrl-C` at the original terminal with a suitable `kill` command? – kjo Apr 04 '13 at 00:04
  • Because how signals works. Shell has special signal handling. When you run a command, the shell forks itself and restore default signal handling, e.g. when you send SIGINT tto the forked process, the process will terminate. But when you running a shell function ON YOUR TERMINAL shell directly (not a forked one) - the shell has special signal handling, e.g. when you press CTRL-C in the keyboard the shell trapping it and handle it in it own way. – clt60 Apr 04 '13 at 12:01
  • check my answer to one other question: http://stackoverflow.com/questions/6108953/how-does-ctrl-c-terminate-a-child-process/6109189#6109189 – clt60 Apr 04 '13 at 12:03