Skip to content

Instantly share code, notes, and snippets.

@cedws
Last active July 5, 2024 13:31
Show Gist options
  • Save cedws/ab8da14beb3aad4521ba6b612956a83e to your computer and use it in GitHub Desktop.
Save cedws/ab8da14beb3aad4521ba6b612956a83e to your computer and use it in GitHub Desktop.
Go-like defer 'keyword' for shell
#!/bin/sh
DEFER=
defer() {
DEFER="$*; ${DEFER}"
trap "{ $DEFER }" EXIT
}
@denishowe
Copy link

denishowe commented Jun 21, 2023

The correct noun form of the adjective "idempotent" is "idempotence", not *idempotency*.
An idempotent operation is one that has the same effect whether you apply it once or multiple times.
Not leaking (which your trap wrapper could help with) is one aspect aspect of this but not the main meaning.

@paride
Copy link

paride commented Jun 21, 2023

This will not handle spaces correctly, try for example

defer echo "a  b" # two spaces between 'a' and 'b'

the output will have one space:

a b

@tkapias
Copy link

tkapias commented Jun 21, 2023

Looking at the ongoing comments on HN and here, I'll go with @charles-dyfis-net's version which handle spaces correctly (bash5.0+ only).

#!/usr/bin/env bash

DEFER=

defer() {
    DEFER="${*@Q}; ${DEFER}"
    trap "{ $DEFER }" EXIT
}

# some tests
TEMP=$(mktemp)
touch "/tmp/a  b"
defer rm -vf "$TEMP" "/tmp/a  b"
ls -lsah "$TEMP"
ls -lsah "/tmp/a  b"

The solutions for shell seemed too complicated or wrong.

@charles-dyfis-net
Copy link

charles-dyfis-net commented Jun 21, 2023

As an addendum (since the suggestions I made above are applicable either to bash 5+ or shells with the ksh93 printf %q extension such as bash or zsh):

For folks who want both safety and compatibility with /bin/sh, I'd suggest encapsulating cleanup code in functions (with no arguments -- getting content from variables only) and then passing the names of those functions to defer. rm_tmp_ab() { rm -vf "$TEMP" "/tmp/a b"; }; defer rm_tmp_ab f/e.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment