Is there a way in bash to guarantee that a trap gets called for cleanup
in a script?
I have a script that works perfectly normally and cleans up after
itself, even if it goes wrong.
However on trying to debug something else, I wanted to run it like this:
./script |& tee log
and now it doesn't clean up if I <ctrl c> it.
Is there a way in bash to guarantee (modulo uncatchable signals) that a cleanup routine gets called?
Is there a way in bash to guarantee that a trap gets called for cleanup
in a script?
On Sat, Sep 28, 2024 at 14:53:10 +0100, Tim Woodall wrote:
Is there a way in bash to guarantee that a trap gets called for cleanup
in a script?
#!/bin/bash
trap cleanup EXIT
cleanup() {
...
}
This works in bash -- i.e., it calls the cleanup function regardless
of whether the shell exits by calling "exit", or by falling off the
end of the script, or by receiving a fatal signal. It does NOT work in /bin/sh (dash, or any other implementation). You have been warned.
On Sat, Sep 28, 2024 at 10:13:59AM -0400, Greg Wooledge wrote:
On Sat, Sep 28, 2024 at 14:53:10 +0100, Tim Woodall wrote:
Is there a way in bash to guarantee that a trap gets called for cleanup in a script?
#!/bin/bash
trap cleanup EXIT
cleanup() {
...
}
This works in bash -- i.e., it calls the cleanup function regardless
of whether the shell exits by calling "exit", or by falling off the
end of the script, or by receiving a fatal signal. It does NOT work in /bin/sh (dash, or any other implementation). You have been warned.
*ALL* scripts MUST start with a #! line as you have given above.
Anyone who does not do that deserves what they get.
On Sat, Sep 28, 2024 at 14:53:10 +0100, Tim Woodall wrote:
Is there a way in bash to guarantee that a trap gets called for cleanup
in a script?
#!/bin/bash
trap cleanup EXIT
cleanup() {
...
}
This works in bash -- i.e., it calls the cleanup function regardless
of whether the shell exits by calling "exit", or by falling off the
end of the script, or by receiving a fatal signal. It does NOT work in /bin/sh (dash, or any other implementation). You have been warned.
Hmmm, I've managed to fix it. The problem seems to be related to using
echo in the exit trap itself while both stderr and stdout are redirected
- e.g. via |& tee log
If I change the echo in the trap function to /bin/echo then it works! (I don't see the output anywhere but that isn't really a problem in this
case - I know why it's aborted!)
I still can't create a small testcase but maybe this gives someone a
clue what issue I'm hitting?
In this particular case it's almost certain that the tee program will
have gone away before the bash script calls the exit handler.
However on trying to debug something else, I wanted to run it like this:
./script |& tee log
and now it doesn't clean up if I <ctrl c> it.
Tim Woodall <debianuser@woodall.me.uk> writes:
However on trying to debug something else, I wanted to run it like this:
./script |& tee log
and now it doesn't clean up if I <ctrl c> it.
Just a point here about tee since I didn't see anyone else mention
it. tee has had the -i option to ignore interrupt signals for ages. The non-obvious side effect is INT signals pass up the pipe, in your case to
bash running your script.
On 28 Sep 2024 16:28 +0100, from debianuser@woodall.me.uk (Tim Woodall):
Hmmm, I've managed to fix it. The problem seems to be related to using
echo in the exit trap itself while both stderr and stdout are redirected
- e.g. via |& tee log
If I change the echo in the trap function to /bin/echo then it works! (I
don't see the output anywhere but that isn't really a problem in this
case - I know why it's aborted!)
I still can't create a small testcase but maybe this gives someone a
clue what issue I'm hitting?
In this particular case it's almost certain that the tee program will
have gone away before the bash script calls the exit handler.
That last sentence seems _very_ relevant here.
If the other end of the pipe is gone, then the shell builtin `echo`
probably fails with SIGPIPE/EPIPE. So will /bin/echo too, of course,
but that will fail just that process, not the script or the /bin/bash
that is executing the script as is probably the case in your
situation. I suspect that if you dig into this, you will find that
everything works as expected up to that `echo` statement.
Check $? after /bin/echo in the handler (probably non-zero), and do a
`type echo` from within a script executed in the same way (probably
"shell builtin").
If so, there's your difference.
On Mon, Sep 30, 2024 at 14:08:19 +0300, Anssi Saari wrote:
Tim Woodall <debianuser@woodall.me.uk> writes:
However on trying to debug something else, I wanted to run it like this: >> >
./script |& tee log
and now it doesn't clean up if I <ctrl c> it.
Just a point here about tee since I didn't see anyone else mention
it. tee has had the -i option to ignore interrupt signals for ages. The
non-obvious side effect is INT signals pass up the pipe, in your case to
bash running your script.
Signals don't "pass up the pipe". A process either receives a signal,
or it doesn't.
Greg Wooledge <greg@wooledge.org> writes:
On Mon, Sep 30, 2024 at 14:08:19 +0300, Anssi Saari wrote:
Tim Woodall <debianuser@woodall.me.uk> writes:
However on trying to debug something else, I wanted to run it like this: >> >
./script |& tee log
and now it doesn't clean up if I <ctrl c> it.
Just a point here about tee since I didn't see anyone else mention
it. tee has had the -i option to ignore interrupt signals for ages. The
non-obvious side effect is INT signals pass up the pipe, in your case to >> bash running your script.
Signals don't "pass up the pipe". A process either receives a signal,
or it doesn't.
I'm not sure now, are you just squabbling over semantics or are you
disputing my description of what happens?
On Tue, Oct 01, 2024 at 01:53:26PM +0300, Anssi Saari wrote:
Greg Wooledge <greg@wooledge.org> writes:
On Mon, Sep 30, 2024 at 14:08:19 +0300, Anssi Saari wrote:
Tim Woodall <debianuser@woodall.me.uk> writes:
However on trying to debug something else, I wanted to run it like this:
./script |& tee log
and now it doesn't clean up if I <ctrl c> it.
Just a point here about tee since I didn't see anyone else mention
it. tee has had the -i option to ignore interrupt signals for ages. The >> >> non-obvious side effect is INT signals pass up the pipe, in your case to >> >> bash running your script.
Signals don't "pass up the pipe". A process either receives a signal,
or it doesn't.
I'm not sure now, are you just squabbling over semantics or are you
disputing my description of what happens?
Explain to us what you really mean by "signals pass up the pipe", then things might become clearer.
<tomas@tuxteam.de> writes:
Explain to us what you really mean by "signals pass up the pipe", then things
might become clearer.
I realize I didn't spell it out. It's the, to me, obvious solution to
Tim's problem, as he wrote:
QUOTE
./script |& tee log
and now it doesn't clean up if I <ctrl c> it."
END QUOTE
Running ./script |& tee -i log works as expected. The script gets the
INT signal and cleans up.
To me, "signal passing up the pipe" is an apt analogy of how things work
in practice.
What I'm mostly trying to convey here, though, is that signals do not *propagate* from one process to another in any kind of automatic way.
On Wed, Oct 02, 2024 at 02:43:11PM +0300, Anssi Saari wrote:
Running ./script |& tee -i log works as expected. The script gets the
INT signal and cleans up.
Understood.
To me, "signal passing up the pipe" is an apt analogy of how things work
in practice.
This triggered the wrong association for me, TBH :)
What actually happens seems completely different to me: the shell
gets the EPIPE from the dying tee before it can see the EINTR, right?
In that case, there are THREE foreground processes which receive a SIGINT:
* The interactive shell, which ignores it, because interactive shells
always ignore SIGINT.
What actually happens seems completely different to me: the shell
gets the EPIPE from the dying tee before it can see the EINTR, right?
<tomas@tuxteam.de> writes:
What actually happens seems completely different to me: the shell
gets the EPIPE from the dying tee before it can see the EINTR, right?
That depends. tee -i will ignore SIGINT but ./script gets it. So it can
keep writing in the pipe, from the script proper or its SIGINT handler
and exit when it's done and that'll kill tee as well.
Without -i, tee will die when it gets a SIGINT, the script gets a
SIGPIPE if it tries to write the pipe now so that would cause the script
to die before its SIGINT handler can finish.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (2 / 14) |
Uptime: | 146:09:33 |
Calls: | 10,383 |
Calls today: | 8 |
Files: | 14,054 |
D/L today: |
2 files (1,861K bytes) |
Messages: | 6,417,699 |