This is the seventh epidose of a small series.
Description:
Pipes, temporary files, lock files, processes spawned in background, rows inserted in a status table that need to be updated… Everything need to be cleaned up if the script exits, even when the exit condition is not triggered inside the script.
BAD:
The worst practice is, of course, to forget to cleanup the tempfiles, leaving my output and temporary directories full of files *.tmp, *.pipe, *.lck, etc. I will not show the code because the list of bad practices is quite long…
Better than forgiving to cleanup, but still very bad, is to cleanup everything just before triggering the exit command (in the following example, F_check_exit is a function that exits the script if the first argument is non-zero, as defined it in the previous episode):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
... some_command_that_must_succeed EXITCODE=$? if [ $EXITCODE -ne 0 ] ; then # Need to exit here, but F_check_exit function does not cleanup correctly [[ $TEMPFILE ]] && [[ -f $TEMPFILE ]] && rm $TMPFILE [[ $EXP_PIPE ]] && [[ -f $EXP_PIPE ]] && rm $EXP_PIPE if [ $CHILD_PID ] ; then ps --pid $CHILD_PID >/dev/null if [ $? -eq 0 ] ; then kill $CHILD_PID # or wait, or what? fi fi F_check_exit $EXITCODE "Some command that must succeed" fi |
A better approach, would be to put all the cleanup tasks in a Cleanup() function and then call this function instead of duplicating all the code everywhere:
1 2 3 4 5 |
... some_command_that_must_succeed EXITCODE=$? [[ $EXITCODE -eq 0 ]] || Cleanup F_check_exit $EXITCODE "Some command that must succeed" |
But still, I need to make sure that I insert this piece of code everywhere. Not optimal yet.
I may include the Cleanup function inside the F_check_exit function, but then I have two inconvenients:
1 – I need to define the Cleanup function in every script that includes my include file
2 – still there will be exit conditions that are not trapped
GOOD:
The good approach would be to trap the EXIT signal with the Cleanup function:
1 2 3 4 5 6 7 8 |
Cleanup() { # cleanup your stuff here } trap Cleanup EXIT do_something F_check_exit $? "Something" |
Much better! But what if my include script has some logic that also creates some temporary files?
I can create a global F_Cleanup function that eventually executes the local Cleanup function, if defined. Let me show this:
Include script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# this is the include file (e.g. $BASEBIN/Init_Env.sh) function F_cleanup() { EXITCODE=$? if [ `typeset -F Cleanup` ] ; then edebug "Cleanup function defined. Executing it..." Cleanup $EXITCODE edebug "Cleanup function executed with return code $?" else edebug "No cleanup function defined." fi # do other global cleanups } ### Register the cleanup function trap F_cleanup EXIT |
Main script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Cleanup: If any function named Cleanup is defined, it will automatically be executed # upon the EXIT signal. Cleanup () { if [ $1 -eq 0 ] ; then # exit 0 trapped else # exit !0 trapped # report the error fi # remove pipes, temporary files etc } . $BASEBIN/Init_Env.sh do_something F_check_exit $? "Something" |
The Cleanup function will be executed only if defined.
No Cleanup function: no worries, but still the F_Cleanup function can do some global cleanup not specific to the main script.
Latest posts by Ludovico (see all)
- New views in Oracle Data Guard 23c - January 3, 2024
- New in Data Guard 21c and 23c: Automatic preparation of the primary - December 22, 2023
- Does FLASHBACK QUERY work across incarnations or after a Data Guard failover? - December 13, 2023
Pingback: Bash tips & tricks [ep. 7]: Cleanup on EXIT with a trap - Ludovico Caldara - Blogs - triBLOG