{"id":1346,"date":"2016-03-24T11:16:43","date_gmt":"2016-03-24T09:16:43","guid":{"rendered":"http:\/\/www.ludovicocaldara.net\/dba\/?p=1346"},"modified":"2016-03-24T11:16:43","modified_gmt":"2016-03-24T09:16:43","slug":"bash-tips-7-cleanup-on-exit","status":"publish","type":"post","link":"https:\/\/www.ludovicocaldara.net\/dba\/bash-tips-7-cleanup-on-exit\/","title":{"rendered":"Bash tips &#038; tricks [ep. 7]: Cleanup on EXIT with a trap"},"content":{"rendered":"<p>This is the seventh epidose of a <a href=\"https:\/\/www.ludovicocaldara.net\/dba\/bash-tips-1-personal-accounts-permissions\/\">small series<\/a>.<\/p>\n<p><strong>Description:<\/strong><\/p>\n<p>Pipes, temporary files, lock files, processes spawned in background, rows inserted in a status table that need to be updated&#8230; Everything need to be cleaned up if the script exits, even when the exit condition is not triggered inside the script.<\/p>\n<p><strong>BAD:<\/strong><\/p>\n<p>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&#8230;<\/p>\n<p>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, <a href=\"https:\/\/www.ludovicocaldara.net\/dba\/bash-tips-6-check-the-exit-code\/\">as defined it in the previous episode<\/a>):<\/p>\n<pre class=\"lang:sh decode:true\">...\r\nsome_command_that_must_succeed\r\nEXITCODE=$?\r\nif [ $EXITCODE -ne 0 ] ; then\r\n    # Need to exit here, but F_check_exit function does not cleanup correctly\r\n    [[ $TEMPFILE ]] &amp;&amp; [[ -f $TEMPFILE ]] &amp;&amp; rm $TMPFILE\r\n    [[ $EXP_PIPE ]] &amp;&amp; [[ -f $EXP_PIPE ]] &amp;&amp; rm $EXP_PIPE\r\n    if [ $CHILD_PID ] ; then\r\n        ps --pid $CHILD_PID &gt;\/dev\/null\r\n        if [ $? -eq 0 ] ; then\r\n            kill $CHILD_PID # or wait, or what?\r\n        fi\r\n    fi\r\n    F_check_exit $EXITCODE \"Some command that must succeed\"\r\nfi<\/pre>\n<p>A better approach, would be to put all the cleanup tasks in a Cleanup()\u00a0 function and then call this function instead of duplicating all the code everywhere:<\/p>\n<pre class=\"lang:sh decode:true \">...\r\nsome_command_that_must_succeed\r\nEXITCODE=$?\r\n[[ $EXITCODE -eq 0 ]] || Cleanup\r\nF_check_exit $EXITCODE \"Some command that must succeed\"<\/pre>\n<p>But still, I need to make sure that I insert this piece of code everywhere. Not optimal yet.<\/p>\n<p>I may include the Cleanup function inside the F_check_exit function, but then I have two inconvenients:<br \/>\n1 &#8211; I need to define the Cleanup function in every script that includes my include file<br \/>\n2 &#8211; still there will be exit conditions that are not trapped<\/p>\n<p><strong>GOOD:<\/strong><\/p>\n<p>The good approach would be to trap the EXIT signal with the Cleanup function:<\/p>\n<pre class=\"lang:sh decode:true\">Cleanup() {\r\n  # cleanup your stuff here\r\n}\r\n\r\ntrap Cleanup EXIT\r\n\r\ndo_something\r\nF_check_exit $? \"Something\"<\/pre>\n<p>Much better! But what if my include script has some logic that also creates some temporary files?<\/p>\n<p>I can create a global F_Cleanup function that eventually executes the local Cleanup function, if defined. Let me show this:<\/p>\n<p>Include script:<\/p>\n<pre class=\"lang:sh decode:true\"># this is the include file (e.g. $BASEBIN\/Init_Env.sh)\r\nfunction F_cleanup() {\r\n        EXITCODE=$?\r\n        if [ `typeset -F Cleanup` ] ; then\r\n                edebug \"Cleanup function defined. Executing it...\"\r\n                Cleanup $EXITCODE\r\n                edebug \"Cleanup function executed with return code $?\"\r\n        else\r\n                edebug \"No cleanup function defined.\"\r\n        fi\r\n        # do other global cleanups\r\n}\r\n\r\n### Register the cleanup function\r\ntrap F_cleanup EXIT<\/pre>\n<p>Main script:<\/p>\n<pre class=\"lang:sh decode:true\"># Cleanup: If any function named Cleanup is defined, it will automatically be executed\r\n# upon the EXIT signal.\r\nCleanup () {\r\n    if [ $1 -eq 0 ] ; then\r\n        # exit 0 trapped\r\n    else\r\n        # exit !0 trapped\r\n        # report the error\r\n    fi\r\n    # remove pipes, temporary files etc\r\n}\r\n\r\n. $BASEBIN\/Init_Env.sh\r\n\r\ndo_something\r\nF_check_exit $? \"Something\"<\/pre>\n<p>The Cleanup function will be executed only if defined.<\/p>\n<p>No Cleanup function: no worries, but still the F_Cleanup function can do some global cleanup not specific to the main script.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8230; Everything need to be cleaned up if the script exits, &hellip; <a href=\"https:\/\/www.ludovicocaldara.net\/dba\/bash-tips-7-cleanup-on-exit\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[5,132],"tags":[],"class_list":["post-1346","post","type-post","status-publish","format-standard","hentry","category-linux","category-triblog"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/posts\/1346","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/comments?post=1346"}],"version-history":[{"count":7,"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/posts\/1346\/revisions"}],"predecessor-version":[{"id":1353,"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/posts\/1346\/revisions\/1353"}],"wp:attachment":[{"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/media?parent=1346"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/categories?post=1346"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ludovicocaldara.net\/dba\/wp-json\/wp\/v2\/tags?post=1346"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}