====== 2022-05-29 - bash and RAII ====== during both ''$DAY_JOB'' and ''$PET_PROJECT'' i often use [[wp>Bash (Unix shell)|bash]]. like A LOT. ;) sometimes to automate some repetitive actions, sometimes to do a quick 1-off task on loads of data... anyway -- there's a lot of ''bash'' involved, typically. to make scripts react properly on errors, i typically start off with: #!/bin/bash set -eu -o pipefail # actual code goes here it's almost like a c&p sequence in my head. it's great as it allows you to catch bugs and errors early and stop the action, before any more damage gets done. the common problem there however is to cleanup stuff afterwards. let's imagine we have some complex logic, that needs 3 temp files to operate, created at different stages of the script. sth along these lines: #!/bin/bash set -eu -o pipefail tmp1=$(mktemp) # do sth with tmp1 tmp2=$(mktemp) # do sth with tmp1 and tmp2 tmp3=$(mktemp) # do sth with tmp1, tmp2 and tmp3 rm -f "$tmp1" "$tmp2" "$tmp3" all good, when script goes well. the problem begins, when sth fails in the middle (eg. after creating 2nd temp file). then we end up with //not// calling ''rm -f ...'' part, and thus leave out garbage. in ''bash'' there is a [[https://ss64.com/bash/trap.html|trap]] statement, that allows to [[https://www.linuxjournal.com/content/bash-trap-command|call some actions on given signals]]... or ''EXIT'' event. so you can do this: #!/bin/bash set -eu -o pipefail tmp1=$(mktemp) tmp2=$(mktemp) tmp3=$(mktemp) trap "rm -f '$tmp1' '$tmp2' '$tmp3'" EXIT # do sth with tmp1 # do sth with tmp1 and tmp2 # do sth with tmp1, tmp2 and tmp3 now all the temp files will auto-cleanup nicely((ok -- it won't , if creating one of the temp files would fail itself. but let's leave that 1 out for a sec.)). there is just one problem with that -- you can have only 1 ''trap'' per signal/event in the script. declaring new one will overwrite the previous. it is therefor all good when you can say upfront what elements do you need to "release". but what if you don't? what if these are runtime-defined? or names are known only any at a certain point in time? there is an excellent paradigm / pattern in [[wp>C++]] called [[wp>RAII]]. while there are no "object destructors" in ''bash'', there are "scopes". in particular you can spawn new shell, in a given "scope", nesting things, effectively giving you as many ''trap''s as you want. example: #!/bin/bash set -eu -o pipefail ( tmp1=$(mktemp) trap "rm '$tmp1'" EXIT # do sth with tmp1 ( tmp2=$(mktemp) trap "rm '$tmp2'" EXIT # do sth with tmp1 and tmp2 ( tmp3=$(mktemp) trap "rm '$tmp3'" EXIT # do sth with tmp1, tmp2 and tmp3 ) ) ) ...and voila! now you can have as many "cleanup" procedures as you want, each being called once resource is "descoped". as a free bonus, it also solves the issue of failure of initialization Nth element, before trap gets registered, like in our 2nd example. i ten to call this pattern ''bash''-RAII -- it tend to work exceptionally well in practice.