I'd like to use a bash EXIT
trap and use exec
to avoid spawning a new process. Is this possible?
That is,
#!/bin/bash
touch $0.$$
trap "rm -v $0.$$" EXIT
/bin/echo Hello
removes the temporary file $0.$$
using bash's EXIT
trap while
#!/bin/bash
touch $0.$$
trap "rm -v $0.$$" EXIT
exec /bin/echo Hello
never "fires" the trap (no message from rm
, file $0.$$
exists after completion).
It, of course, makes sense that the trap can't fire as bash is no longer in control after the exec
. Is there some way to make it work and use exec
? This is admittedly out of curiosity moreso than practical matters.
Generally, no. This is not possible for the reason you mention.
That's a boring answer though. Let's look at our options for workarounds:
If we care more about exec semantics and less about starting multiple processes, we can for arbitrary executables do:
{ while kill -0 $$; do sleep 5; done; rm "$0.$$"; } &
exec ./file
which will exec
the file and have another process polling it and doing cleanup when it's done.
If we want to avoid forks and what we're executing is another shell script, we can do
exec bash --rcfile <(echo 'trap "..." exit') -i ./file
to exec
the file and do the cleanup afterwards (as long as the script doesn't exec
or override the trap), without starting a new process. source
ing instead of exec
ing will have much the same effect:
trap "..." exit
source ./file
If we want to get really hacky, we can use LD_PRELOAD
to override exit(3)
and run a command of our choice:
#include <stdlib.h>
void exit(int c) {
char* cmd = getenv("EXIT");
char *argv[] = { "bash", "-c", cmd, NULL };
char *envp[] = { NULL };
execvpe("bash", argv, envp);
}
We can compile this as a library:
$ gcc -shared -fPIC foo.c -o libfoo.so
and then preload it into arbitrary, dynamically linked executables:
$ LD_PRELOAD=./libfoo.so EXIT='echo "This is a hack"' ls *foo*
foo.c libfoo.so
This is a hack
These hacks are fun, but rarely necessary in the real world. The simpler, better and more canonical solution is just to not exec
.