An atexit for Bash











up vote
7
down vote

favorite












I wrote the following atexit implementation for Bash



#! /bin/bash

set -e

ATEXIT=()

function atexit_handler
{
local EXPR
for EXPR in "${ATEXIT[@]}"; do
echo "evaluating $EXPR"
eval "$EXPR" || true
done
}

trap atexit_handler EXIT

function atexit ()
{
local EXPR
for EXPR in "$@"; do
ATEXIT+=("$EXPR")
done
}

atexit true
atexit false
atexit "echo bye"

false


I am wondering, if this is possible without the use of eval.










share|improve this question


























    up vote
    7
    down vote

    favorite












    I wrote the following atexit implementation for Bash



    #! /bin/bash

    set -e

    ATEXIT=()

    function atexit_handler
    {
    local EXPR
    for EXPR in "${ATEXIT[@]}"; do
    echo "evaluating $EXPR"
    eval "$EXPR" || true
    done
    }

    trap atexit_handler EXIT

    function atexit ()
    {
    local EXPR
    for EXPR in "$@"; do
    ATEXIT+=("$EXPR")
    done
    }

    atexit true
    atexit false
    atexit "echo bye"

    false


    I am wondering, if this is possible without the use of eval.










    share|improve this question
























      up vote
      7
      down vote

      favorite









      up vote
      7
      down vote

      favorite











      I wrote the following atexit implementation for Bash



      #! /bin/bash

      set -e

      ATEXIT=()

      function atexit_handler
      {
      local EXPR
      for EXPR in "${ATEXIT[@]}"; do
      echo "evaluating $EXPR"
      eval "$EXPR" || true
      done
      }

      trap atexit_handler EXIT

      function atexit ()
      {
      local EXPR
      for EXPR in "$@"; do
      ATEXIT+=("$EXPR")
      done
      }

      atexit true
      atexit false
      atexit "echo bye"

      false


      I am wondering, if this is possible without the use of eval.










      share|improve this question













      I wrote the following atexit implementation for Bash



      #! /bin/bash

      set -e

      ATEXIT=()

      function atexit_handler
      {
      local EXPR
      for EXPR in "${ATEXIT[@]}"; do
      echo "evaluating $EXPR"
      eval "$EXPR" || true
      done
      }

      trap atexit_handler EXIT

      function atexit ()
      {
      local EXPR
      for EXPR in "$@"; do
      ATEXIT+=("$EXPR")
      done
      }

      atexit true
      atexit false
      atexit "echo bye"

      false


      I am wondering, if this is possible without the use of eval.







      bash






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Oct 21 '14 at 9:07









      ceving

      23517




      23517






















          3 Answers
          3






          active

          oldest

          votes

















          up vote
          7
          down vote













          I don't think it's possible without the eval. A close candidate might be:



          atexit_handler() {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          $EXPR || true
          done
          }


          But this won't work with non-trivial expressions like this:



          atexit 'for i in "a b" c; do echo $i; done'




          Using the function keyword in function declaration like this is an outdated practice:




          function atexit_handler
          {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          eval "$EXPR" || true
          done
          }



          Use the more modern style I wrote in the previous example above.





          Instead of this:




            for EXPR in "$@"; do
          ATEXIT+=("$EXPR")
          done



          A simpler way to iterate over $@:



            for EXPR; do
          ATEXIT+=("$EXPR")
          done


          But actually, as @etan-reisner pointed out in a comment, it's silly to loop here when you can add the entire arg list in one swift move:



          ATEXIT+=("$@")





          share|improve this answer



















          • 2




            Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@")
            – Etan Reisner
            Oct 23 '14 at 18:45






          • 1




            Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not.
            – Etan Reisner
            Oct 23 '14 at 18:46










          • @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second.
            – janos
            Oct 23 '14 at 20:34










          • Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions.
            – ceving
            Oct 27 '14 at 9:51










          • At the time I posted this, I didn't know what EXIT really meant. Thanks @TobySpeight for pointing this out, dropped that point. (I fear I may have said things like this in other old answers too. I should probably search and verify all :-/)
            – janos
            yesterday


















          up vote
          1
          down vote













          Sorry for excavating this, but just found it and wanted to try.



          Will offer what I can.



          #! /bin/bash


          This is no crime, but it's a tad more portable (if that matters at all to you) if you use



          #! /bin/env bash


          env itself is virtually always in /bin, though some installations move the interpreter binaries.



          I don't personally like or use set -e. It doesn't give me as much control over what happens. Personally, I prefer traps, something like this:



          trap 'echo "ERROR $? at $0:$LINENO - [$BASH_COMMAND]"' err


          Then I get useful feedback.



          $: bogus
          bash: bogus: command not found
          ERROR 127 at /usr/bin/bash:73 - [bogus]


          As for the eval, how about creating a controlled file to source at exit time?



          atexit_handler() {
          tmp=$(mktemp)
          printf "%sn" "${ATEXIT[@]}" >| $tmp
          trap 'echo "Error $? in exit handler when processing [$BASH_COMMAND]"' err
          . $tmp
          }


          This would allow for more arbitrarily complex possibilities, without really worsening the security risk of the eval, which isn't catastrophic if your code registers all the commands going into the handler anyway. It also leaves you the option of scheduling the cleanup of the file with the handler, so that if it succeeds it leaves no mess, but if it fails will abort and let you debug the file.



          atexit true
          atexit "echo bye"
          atexit 'sing() { for i in do re me;do echo "$i"; done; }'
          atexit sing
          atexit false
          atexit 'rm $tmp' # defer evaluation


          I love this idea, btw. I may start using it. :)






          share|improve this answer










          New contributor




          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















          • I've never seen a /bin/env - on Debian-based systems at least, it's always in /usr/bin.
            – Toby Speight
            yesterday










          • As long as it's working, I wouldn't sweat it. YMMV; even where I work, across a lot of systems, they are mostly all RHEL 6 or 7 and vaguely similar in structure, so it has rarely been an issue. It's just something I have been told to cultivate as a habit, and I have (anecdotally) had fewer issues since I started doing it. shrug :D
            – Paul Hodges
            yesterday










          • So RHEL have /bin/env instead of /usr/bin/env? Or as well as? If it has both, then the latter would seem to be the safer choice.
            – Toby Speight
            yesterday












          • Common to have it linked in both locations just in case. :)
            – Paul Hodges
            yesterday


















          up vote
          0
          down vote













          Still uses eval, but it seems cleaner to do:



          atexit() {
          cmd="$1"
          eval set -- $(trap -p EXIT)
          trap "$cmd; $3" EXIT
          }


          To allow semi-colons (eg, atexit "echo foo;") and aesthetic purity, you might like:



          atexit() {
          cmd="${1%;}"
          eval set -- $(trap -p EXIT)
          trap "$cmd${3:+; }$3" EXIT
          }





          share|improve this answer























          • You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer.
            – Toby Speight
            yesterday











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f67417%2fan-atexit-for-bash%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          3 Answers
          3






          active

          oldest

          votes








          3 Answers
          3






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          7
          down vote













          I don't think it's possible without the eval. A close candidate might be:



          atexit_handler() {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          $EXPR || true
          done
          }


          But this won't work with non-trivial expressions like this:



          atexit 'for i in "a b" c; do echo $i; done'




          Using the function keyword in function declaration like this is an outdated practice:




          function atexit_handler
          {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          eval "$EXPR" || true
          done
          }



          Use the more modern style I wrote in the previous example above.





          Instead of this:




            for EXPR in "$@"; do
          ATEXIT+=("$EXPR")
          done



          A simpler way to iterate over $@:



            for EXPR; do
          ATEXIT+=("$EXPR")
          done


          But actually, as @etan-reisner pointed out in a comment, it's silly to loop here when you can add the entire arg list in one swift move:



          ATEXIT+=("$@")





          share|improve this answer



















          • 2




            Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@")
            – Etan Reisner
            Oct 23 '14 at 18:45






          • 1




            Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not.
            – Etan Reisner
            Oct 23 '14 at 18:46










          • @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second.
            – janos
            Oct 23 '14 at 20:34










          • Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions.
            – ceving
            Oct 27 '14 at 9:51










          • At the time I posted this, I didn't know what EXIT really meant. Thanks @TobySpeight for pointing this out, dropped that point. (I fear I may have said things like this in other old answers too. I should probably search and verify all :-/)
            – janos
            yesterday















          up vote
          7
          down vote













          I don't think it's possible without the eval. A close candidate might be:



          atexit_handler() {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          $EXPR || true
          done
          }


          But this won't work with non-trivial expressions like this:



          atexit 'for i in "a b" c; do echo $i; done'




          Using the function keyword in function declaration like this is an outdated practice:




          function atexit_handler
          {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          eval "$EXPR" || true
          done
          }



          Use the more modern style I wrote in the previous example above.





          Instead of this:




            for EXPR in "$@"; do
          ATEXIT+=("$EXPR")
          done



          A simpler way to iterate over $@:



            for EXPR; do
          ATEXIT+=("$EXPR")
          done


          But actually, as @etan-reisner pointed out in a comment, it's silly to loop here when you can add the entire arg list in one swift move:



          ATEXIT+=("$@")





          share|improve this answer



















          • 2




            Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@")
            – Etan Reisner
            Oct 23 '14 at 18:45






          • 1




            Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not.
            – Etan Reisner
            Oct 23 '14 at 18:46










          • @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second.
            – janos
            Oct 23 '14 at 20:34










          • Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions.
            – ceving
            Oct 27 '14 at 9:51










          • At the time I posted this, I didn't know what EXIT really meant. Thanks @TobySpeight for pointing this out, dropped that point. (I fear I may have said things like this in other old answers too. I should probably search and verify all :-/)
            – janos
            yesterday













          up vote
          7
          down vote










          up vote
          7
          down vote









          I don't think it's possible without the eval. A close candidate might be:



          atexit_handler() {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          $EXPR || true
          done
          }


          But this won't work with non-trivial expressions like this:



          atexit 'for i in "a b" c; do echo $i; done'




          Using the function keyword in function declaration like this is an outdated practice:




          function atexit_handler
          {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          eval "$EXPR" || true
          done
          }



          Use the more modern style I wrote in the previous example above.





          Instead of this:




            for EXPR in "$@"; do
          ATEXIT+=("$EXPR")
          done



          A simpler way to iterate over $@:



            for EXPR; do
          ATEXIT+=("$EXPR")
          done


          But actually, as @etan-reisner pointed out in a comment, it's silly to loop here when you can add the entire arg list in one swift move:



          ATEXIT+=("$@")





          share|improve this answer














          I don't think it's possible without the eval. A close candidate might be:



          atexit_handler() {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          $EXPR || true
          done
          }


          But this won't work with non-trivial expressions like this:



          atexit 'for i in "a b" c; do echo $i; done'




          Using the function keyword in function declaration like this is an outdated practice:




          function atexit_handler
          {
          local EXPR
          for EXPR in "${ATEXIT[@]}"; do
          echo "evaluating $EXPR"
          eval "$EXPR" || true
          done
          }



          Use the more modern style I wrote in the previous example above.





          Instead of this:




            for EXPR in "$@"; do
          ATEXIT+=("$EXPR")
          done



          A simpler way to iterate over $@:



            for EXPR; do
          ATEXIT+=("$EXPR")
          done


          But actually, as @etan-reisner pointed out in a comment, it's silly to loop here when you can add the entire arg list in one swift move:



          ATEXIT+=("$@")






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited yesterday

























          answered Oct 21 '14 at 11:37









          janos

          96.6k12124350




          96.6k12124350








          • 2




            Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@")
            – Etan Reisner
            Oct 23 '14 at 18:45






          • 1




            Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not.
            – Etan Reisner
            Oct 23 '14 at 18:46










          • @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second.
            – janos
            Oct 23 '14 at 20:34










          • Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions.
            – ceving
            Oct 27 '14 at 9:51










          • At the time I posted this, I didn't know what EXIT really meant. Thanks @TobySpeight for pointing this out, dropped that point. (I fear I may have said things like this in other old answers too. I should probably search and verify all :-/)
            – janos
            yesterday














          • 2




            Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@")
            – Etan Reisner
            Oct 23 '14 at 18:45






          • 1




            Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not.
            – Etan Reisner
            Oct 23 '14 at 18:46










          • @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second.
            – janos
            Oct 23 '14 at 20:34










          • Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions.
            – ceving
            Oct 27 '14 at 9:51










          • At the time I posted this, I didn't know what EXIT really meant. Thanks @TobySpeight for pointing this out, dropped that point. (I fear I may have said things like this in other old answers too. I should probably search and verify all :-/)
            – janos
            yesterday








          2




          2




          Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@")
          – Etan Reisner
          Oct 23 '14 at 18:45




          Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@")
          – Etan Reisner
          Oct 23 '14 at 18:45




          1




          1




          Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not.
          – Etan Reisner
          Oct 23 '14 at 18:46




          Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not.
          – Etan Reisner
          Oct 23 '14 at 18:46












          @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second.
          – janos
          Oct 23 '14 at 20:34




          @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second.
          – janos
          Oct 23 '14 at 20:34












          Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions.
          – ceving
          Oct 27 '14 at 9:51




          Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions.
          – ceving
          Oct 27 '14 at 9:51












          At the time I posted this, I didn't know what EXIT really meant. Thanks @TobySpeight for pointing this out, dropped that point. (I fear I may have said things like this in other old answers too. I should probably search and verify all :-/)
          – janos
          yesterday




          At the time I posted this, I didn't know what EXIT really meant. Thanks @TobySpeight for pointing this out, dropped that point. (I fear I may have said things like this in other old answers too. I should probably search and verify all :-/)
          – janos
          yesterday












          up vote
          1
          down vote













          Sorry for excavating this, but just found it and wanted to try.



          Will offer what I can.



          #! /bin/bash


          This is no crime, but it's a tad more portable (if that matters at all to you) if you use



          #! /bin/env bash


          env itself is virtually always in /bin, though some installations move the interpreter binaries.



          I don't personally like or use set -e. It doesn't give me as much control over what happens. Personally, I prefer traps, something like this:



          trap 'echo "ERROR $? at $0:$LINENO - [$BASH_COMMAND]"' err


          Then I get useful feedback.



          $: bogus
          bash: bogus: command not found
          ERROR 127 at /usr/bin/bash:73 - [bogus]


          As for the eval, how about creating a controlled file to source at exit time?



          atexit_handler() {
          tmp=$(mktemp)
          printf "%sn" "${ATEXIT[@]}" >| $tmp
          trap 'echo "Error $? in exit handler when processing [$BASH_COMMAND]"' err
          . $tmp
          }


          This would allow for more arbitrarily complex possibilities, without really worsening the security risk of the eval, which isn't catastrophic if your code registers all the commands going into the handler anyway. It also leaves you the option of scheduling the cleanup of the file with the handler, so that if it succeeds it leaves no mess, but if it fails will abort and let you debug the file.



          atexit true
          atexit "echo bye"
          atexit 'sing() { for i in do re me;do echo "$i"; done; }'
          atexit sing
          atexit false
          atexit 'rm $tmp' # defer evaluation


          I love this idea, btw. I may start using it. :)






          share|improve this answer










          New contributor




          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















          • I've never seen a /bin/env - on Debian-based systems at least, it's always in /usr/bin.
            – Toby Speight
            yesterday










          • As long as it's working, I wouldn't sweat it. YMMV; even where I work, across a lot of systems, they are mostly all RHEL 6 or 7 and vaguely similar in structure, so it has rarely been an issue. It's just something I have been told to cultivate as a habit, and I have (anecdotally) had fewer issues since I started doing it. shrug :D
            – Paul Hodges
            yesterday










          • So RHEL have /bin/env instead of /usr/bin/env? Or as well as? If it has both, then the latter would seem to be the safer choice.
            – Toby Speight
            yesterday












          • Common to have it linked in both locations just in case. :)
            – Paul Hodges
            yesterday















          up vote
          1
          down vote













          Sorry for excavating this, but just found it and wanted to try.



          Will offer what I can.



          #! /bin/bash


          This is no crime, but it's a tad more portable (if that matters at all to you) if you use



          #! /bin/env bash


          env itself is virtually always in /bin, though some installations move the interpreter binaries.



          I don't personally like or use set -e. It doesn't give me as much control over what happens. Personally, I prefer traps, something like this:



          trap 'echo "ERROR $? at $0:$LINENO - [$BASH_COMMAND]"' err


          Then I get useful feedback.



          $: bogus
          bash: bogus: command not found
          ERROR 127 at /usr/bin/bash:73 - [bogus]


          As for the eval, how about creating a controlled file to source at exit time?



          atexit_handler() {
          tmp=$(mktemp)
          printf "%sn" "${ATEXIT[@]}" >| $tmp
          trap 'echo "Error $? in exit handler when processing [$BASH_COMMAND]"' err
          . $tmp
          }


          This would allow for more arbitrarily complex possibilities, without really worsening the security risk of the eval, which isn't catastrophic if your code registers all the commands going into the handler anyway. It also leaves you the option of scheduling the cleanup of the file with the handler, so that if it succeeds it leaves no mess, but if it fails will abort and let you debug the file.



          atexit true
          atexit "echo bye"
          atexit 'sing() { for i in do re me;do echo "$i"; done; }'
          atexit sing
          atexit false
          atexit 'rm $tmp' # defer evaluation


          I love this idea, btw. I may start using it. :)






          share|improve this answer










          New contributor




          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















          • I've never seen a /bin/env - on Debian-based systems at least, it's always in /usr/bin.
            – Toby Speight
            yesterday










          • As long as it's working, I wouldn't sweat it. YMMV; even where I work, across a lot of systems, they are mostly all RHEL 6 or 7 and vaguely similar in structure, so it has rarely been an issue. It's just something I have been told to cultivate as a habit, and I have (anecdotally) had fewer issues since I started doing it. shrug :D
            – Paul Hodges
            yesterday










          • So RHEL have /bin/env instead of /usr/bin/env? Or as well as? If it has both, then the latter would seem to be the safer choice.
            – Toby Speight
            yesterday












          • Common to have it linked in both locations just in case. :)
            – Paul Hodges
            yesterday













          up vote
          1
          down vote










          up vote
          1
          down vote









          Sorry for excavating this, but just found it and wanted to try.



          Will offer what I can.



          #! /bin/bash


          This is no crime, but it's a tad more portable (if that matters at all to you) if you use



          #! /bin/env bash


          env itself is virtually always in /bin, though some installations move the interpreter binaries.



          I don't personally like or use set -e. It doesn't give me as much control over what happens. Personally, I prefer traps, something like this:



          trap 'echo "ERROR $? at $0:$LINENO - [$BASH_COMMAND]"' err


          Then I get useful feedback.



          $: bogus
          bash: bogus: command not found
          ERROR 127 at /usr/bin/bash:73 - [bogus]


          As for the eval, how about creating a controlled file to source at exit time?



          atexit_handler() {
          tmp=$(mktemp)
          printf "%sn" "${ATEXIT[@]}" >| $tmp
          trap 'echo "Error $? in exit handler when processing [$BASH_COMMAND]"' err
          . $tmp
          }


          This would allow for more arbitrarily complex possibilities, without really worsening the security risk of the eval, which isn't catastrophic if your code registers all the commands going into the handler anyway. It also leaves you the option of scheduling the cleanup of the file with the handler, so that if it succeeds it leaves no mess, but if it fails will abort and let you debug the file.



          atexit true
          atexit "echo bye"
          atexit 'sing() { for i in do re me;do echo "$i"; done; }'
          atexit sing
          atexit false
          atexit 'rm $tmp' # defer evaluation


          I love this idea, btw. I may start using it. :)






          share|improve this answer










          New contributor




          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.









          Sorry for excavating this, but just found it and wanted to try.



          Will offer what I can.



          #! /bin/bash


          This is no crime, but it's a tad more portable (if that matters at all to you) if you use



          #! /bin/env bash


          env itself is virtually always in /bin, though some installations move the interpreter binaries.



          I don't personally like or use set -e. It doesn't give me as much control over what happens. Personally, I prefer traps, something like this:



          trap 'echo "ERROR $? at $0:$LINENO - [$BASH_COMMAND]"' err


          Then I get useful feedback.



          $: bogus
          bash: bogus: command not found
          ERROR 127 at /usr/bin/bash:73 - [bogus]


          As for the eval, how about creating a controlled file to source at exit time?



          atexit_handler() {
          tmp=$(mktemp)
          printf "%sn" "${ATEXIT[@]}" >| $tmp
          trap 'echo "Error $? in exit handler when processing [$BASH_COMMAND]"' err
          . $tmp
          }


          This would allow for more arbitrarily complex possibilities, without really worsening the security risk of the eval, which isn't catastrophic if your code registers all the commands going into the handler anyway. It also leaves you the option of scheduling the cleanup of the file with the handler, so that if it succeeds it leaves no mess, but if it fails will abort and let you debug the file.



          atexit true
          atexit "echo bye"
          atexit 'sing() { for i in do re me;do echo "$i"; done; }'
          atexit sing
          atexit false
          atexit 'rm $tmp' # defer evaluation


          I love this idea, btw. I may start using it. :)







          share|improve this answer










          New contributor




          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.









          share|improve this answer



          share|improve this answer








          edited yesterday





















          New contributor




          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.









          answered 2 days ago









          Paul Hodges

          1114




          1114




          New contributor




          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.





          New contributor





          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.






          Paul Hodges is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.












          • I've never seen a /bin/env - on Debian-based systems at least, it's always in /usr/bin.
            – Toby Speight
            yesterday










          • As long as it's working, I wouldn't sweat it. YMMV; even where I work, across a lot of systems, they are mostly all RHEL 6 or 7 and vaguely similar in structure, so it has rarely been an issue. It's just something I have been told to cultivate as a habit, and I have (anecdotally) had fewer issues since I started doing it. shrug :D
            – Paul Hodges
            yesterday










          • So RHEL have /bin/env instead of /usr/bin/env? Or as well as? If it has both, then the latter would seem to be the safer choice.
            – Toby Speight
            yesterday












          • Common to have it linked in both locations just in case. :)
            – Paul Hodges
            yesterday


















          • I've never seen a /bin/env - on Debian-based systems at least, it's always in /usr/bin.
            – Toby Speight
            yesterday










          • As long as it's working, I wouldn't sweat it. YMMV; even where I work, across a lot of systems, they are mostly all RHEL 6 or 7 and vaguely similar in structure, so it has rarely been an issue. It's just something I have been told to cultivate as a habit, and I have (anecdotally) had fewer issues since I started doing it. shrug :D
            – Paul Hodges
            yesterday










          • So RHEL have /bin/env instead of /usr/bin/env? Or as well as? If it has both, then the latter would seem to be the safer choice.
            – Toby Speight
            yesterday












          • Common to have it linked in both locations just in case. :)
            – Paul Hodges
            yesterday
















          I've never seen a /bin/env - on Debian-based systems at least, it's always in /usr/bin.
          – Toby Speight
          yesterday




          I've never seen a /bin/env - on Debian-based systems at least, it's always in /usr/bin.
          – Toby Speight
          yesterday












          As long as it's working, I wouldn't sweat it. YMMV; even where I work, across a lot of systems, they are mostly all RHEL 6 or 7 and vaguely similar in structure, so it has rarely been an issue. It's just something I have been told to cultivate as a habit, and I have (anecdotally) had fewer issues since I started doing it. shrug :D
          – Paul Hodges
          yesterday




          As long as it's working, I wouldn't sweat it. YMMV; even where I work, across a lot of systems, they are mostly all RHEL 6 or 7 and vaguely similar in structure, so it has rarely been an issue. It's just something I have been told to cultivate as a habit, and I have (anecdotally) had fewer issues since I started doing it. shrug :D
          – Paul Hodges
          yesterday












          So RHEL have /bin/env instead of /usr/bin/env? Or as well as? If it has both, then the latter would seem to be the safer choice.
          – Toby Speight
          yesterday






          So RHEL have /bin/env instead of /usr/bin/env? Or as well as? If it has both, then the latter would seem to be the safer choice.
          – Toby Speight
          yesterday














          Common to have it linked in both locations just in case. :)
          – Paul Hodges
          yesterday




          Common to have it linked in both locations just in case. :)
          – Paul Hodges
          yesterday










          up vote
          0
          down vote













          Still uses eval, but it seems cleaner to do:



          atexit() {
          cmd="$1"
          eval set -- $(trap -p EXIT)
          trap "$cmd; $3" EXIT
          }


          To allow semi-colons (eg, atexit "echo foo;") and aesthetic purity, you might like:



          atexit() {
          cmd="${1%;}"
          eval set -- $(trap -p EXIT)
          trap "$cmd${3:+; }$3" EXIT
          }





          share|improve this answer























          • You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer.
            – Toby Speight
            yesterday















          up vote
          0
          down vote













          Still uses eval, but it seems cleaner to do:



          atexit() {
          cmd="$1"
          eval set -- $(trap -p EXIT)
          trap "$cmd; $3" EXIT
          }


          To allow semi-colons (eg, atexit "echo foo;") and aesthetic purity, you might like:



          atexit() {
          cmd="${1%;}"
          eval set -- $(trap -p EXIT)
          trap "$cmd${3:+; }$3" EXIT
          }





          share|improve this answer























          • You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer.
            – Toby Speight
            yesterday













          up vote
          0
          down vote










          up vote
          0
          down vote









          Still uses eval, but it seems cleaner to do:



          atexit() {
          cmd="$1"
          eval set -- $(trap -p EXIT)
          trap "$cmd; $3" EXIT
          }


          To allow semi-colons (eg, atexit "echo foo;") and aesthetic purity, you might like:



          atexit() {
          cmd="${1%;}"
          eval set -- $(trap -p EXIT)
          trap "$cmd${3:+; }$3" EXIT
          }





          share|improve this answer














          Still uses eval, but it seems cleaner to do:



          atexit() {
          cmd="$1"
          eval set -- $(trap -p EXIT)
          trap "$cmd; $3" EXIT
          }


          To allow semi-colons (eg, atexit "echo foo;") and aesthetic purity, you might like:



          atexit() {
          cmd="${1%;}"
          eval set -- $(trap -p EXIT)
          trap "$cmd${3:+; }$3" EXIT
          }






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Aug 29 '16 at 17:47

























          answered Aug 29 '16 at 17:23









          William Pursell

          1185




          1185












          • You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer.
            – Toby Speight
            yesterday


















          • You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer.
            – Toby Speight
            yesterday
















          You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer.
          – Toby Speight
          yesterday




          You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer.
          – Toby Speight
          yesterday


















           

          draft saved


          draft discarded



















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f67417%2fan-atexit-for-bash%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Morgemoulin

          Scott Moir

          Souastre