-
-
Save cedws/ab8da14beb3aad4521ba6b612956a83e to your computer and use it in GitHub Desktop.
#!/bin/sh | |
DEFER= | |
defer() { | |
DEFER="$*; ${DEFER}" | |
trap "{ $DEFER }" EXIT | |
} |
This is very prone to shell injection vulnerabilities as it's currently written.
With bash 5.0, you can mitigate that using ${*@Q}
in place of $*
.
With earlier releases, you can use printf -v cmd_q '%q ' "$@"
to generate an eval
-safe escaping of an arbitrary command (stored in the variable $cmd_q
).
Also, consider staying out of the all-caps namespace; as POSIX advises at https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, all-caps names are used for variables that reflect or modify behavior of the shell and other POSIX-specified tools. (Because regular shell variables and environment variables share a single namespace -- setting a shell variable will overwrite any like-named environment variable -- the same conventions apply to both).
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.
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
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.
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.
Yep', I confirm it's fixed. Thanks. :)