6 ms·
Defer for Shell (2020)
- smitty1e 3y agoThis seems conceptually similar to python's `with` statement. https://docs.python.org/3/reference/compound_stmts.html#the-with-statement https://docs.python.org/3/reference/compound_stmts.html#the-...
- joeblubaugh 3y agoYeah, with also provides a nice finalization call, but there are a few differences: - with requires the subject to implement a particular interface. - multiple with calls either nest, or they execute in the order they’re written, as soon as you exit the block. Multiple withs nest uncomfortably deep. Go’s defer, and this she’ll one, turn statements into a stack where the past defer executes first on exit.
- remram 3y agocontextlib from the standard library, specifically contextlib.ExitStack, provide the rest of the patterns you might want to use with 'with'. Including removing the need for nested 'with' block, and non-lexical finalization orders.
- remram 3y agoMore like C and Python's atexit
- nomel 3y agoC: https://en.cppreference.com/w/c/program/atexit https://en.cppreference.com/w/c/program/atexit Python: https://docs.python.org/3/library/atexit.html https://docs.python.org/3/library/atexit.html TIL for python. I've only used signal: https://docs.python.org/3/library/signal.html https://docs.python.org/3/library/signal.html
- archargelod 3y agoI know, so far, only two languages that use defer: Go and Nim.
- commotionfever 3y agoand harelang.org
- wearhere 3y agoAlso Swift!
- Jtsummers 3y agoSwift, but it is bound to narrower scopes, not just the function scope like Go. How does Nim scope defer statements?
- archargelod 3y agoIn Nim, defer is a macro, that rewrites scope into "try: finally:" statement. 'finally' code block is always executed, even if there were any exceptions. https://nim-lang.org/docs/manual.html#exception-handling-defer-statement https://nim-lang.org/docs/manual.html#exception-handling-def...
- Jtsummers 3y agoYeah, my main question was about the scope of the defer but it appears that it's block scoped like Swift, not function scoped like Go.
- hiyer 3y agoZig has it too.
- loeg 3y agoC++ with folly's SCOPE_EXIT {} construct: https://github.com/facebook/folly/blob/main/folly/ScopeGuard.h#L310 https://github.com/facebook/folly/blob/main/folly/ScopeGuard...
- 3y ago
- _huayra_ 3y agoI'm adding this to my dotfiles because it just seems so dang useful, but this really seems to be the intersection of elegance and hackery that's the epitome of shell scripting. A close second would be that "fork bomb" that's just a bunch of :{}|{} or something weird like that, but `defer` here is actually useful!
- panki27 3y ago:() { :|:& };: It's easy to read if you know what's happening - it's defining a function called ":"
- throwaway106382 3y agowell damn, I never really did look into why this is a forkbomb but this just made it extremely clear in one short sentence. thanks
- JohnMakin 3y agoI’m trying to figure out a way this can backfire horribly but falling short of it. Curious to test the interaction with set -e if your defer command also errors. Very clever.
- adrianmonk 3y agoWell, for one thing, it doesn't quote arguments correctly. If you do this... touch "a b" ...it will create one file called "a b". But if you do this... defer touch "a b" ...it will create two files called "a" and "b".
- remram 3y agoYou mean it doesn't quote arguments automatically, it is safe so long as you always quote the command defer 'touch "a b"' (a usability pitfall I know, but probably not fixable easily) edit: maybe escaping could be added though, using sed? DEFER="; ${DEFER}" for arg in "$@"; do DEFER="$(printf -- %s "${arg}" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/") ${DEFER}" done
- kazinator 3y ago
- abathur 3y agoHeh! Not sure if it's a cosmic coincidence, but I posted something (https://news.ycombinator.com/item?id=36410781 https://news.ycombinator.com/item?id=36410781) similar based on https://github.com/bashup/events https://github.com/bashup/events a few hours ago in another thread. The version here's much simpler, which has its merit if you need the performance or have a simple (append-only, exit-only) use-case. bashup.events supports removing handlers, one-time events, pseudo-promises, and more. It's easy to use it for something like this, but also modular deferred init/load and more. (linked comment has a little more detail on how I use it.)
- vermilingua 3y ago> This is known as idempotency. No? Idempotency is ensuring that calling the same function with the same input N times will have the exact same effect as calling it once. Adding this defer does _not_ guarantee idempotency, because it has no impact on the body of the script. You can provide just as much idempotency by running `rm -rf /tmp/$script_dir*` on the first line.
- dizhn 3y agohttps://arslan.io/2019/07/03/how-to-write-idempotent-bash-scripts/ https://arslan.io/2019/07/03/how-to-write-idempotent-bash-sc... I am not sure how autoritative this is but I immediately thought of this post. (It's the vim-go guy)
- mplewis9z 3y agoWhile this is great, this only cleans up temporary files on a clean exit (well, depending on the shell), and no trap can catch SIGKILL. I much prefer cleaning up my files _before_ I use them, like: ``` touch 'temp.txt' exec 3 < 'temp.txt' exec 4 > 'temp.txt' rm -f 'temp.txt' ``` Now I have file descriptors 3 and 4 available for reading and writing, respectively, and even if I pulled the plug out from my desktop the file will not be anywhere on my filesystem.
- m1keil 3y agoIf I understand correctly, this works more like a pipe and not a file in the sense that you can only read what you wrote once, right? But a nice trick nonetheless.
- mplewis9z 3y ago> you can only read what you wrote once In the way I’ve written it, yes. But you can open as many file descriptors as you need before you `rm` the file, and they each will keep their own position in the file. You can’t do a dynamic number of reads through the file (at least without dumping the contents somewhere else first), but a fixed number greater than one is absolutely possible.
- m1keil 3y agoAh, nice. Thanks!
- hkwerf 3y agoSame here. I'm maintaining a script which generate a file, sets up a loopback device and dmcrypt, then mounts the resulting map. Every of these operations but the mount can be done in a way that the kernel will remove them once they're not needed anymore, i.e. unmounting the filesystem leads to the dm-crypt map being discarded, leads to the loopback device being removed and the space reserved by the file being freed. This even persists past the exit of the script.
- mbwgh 3y ago
- deleted 3y ago[deleted]
- ceving 3y agoI wrote something similar some years ago: https://codereview.stackexchange.com/questions/67417/an-atexit-for-bash https://codereview.stackexchange.com/questions/67417/an-atex... I do not like the way you build expressions by concatenating strings. This is the driving force behind SQL injections.
- mplewis9z 3y ago> I do not like the way you build expressions by concatenating strings. Unfortunately this is the only option if you're trying to preserve POSIX shell compatibility - arrays are implementation-specific additions to the specifications. I do not like the way Bash has become synonymous with shell, and appreciate people making an effort to make their scripts work across a large variety of different systems.