Skip to content

Instantly share code, notes, and snippets.

@cedws
Last active June 23, 2023 13:26
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • 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
}
@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