I think that's certainly a fair option, and a potential solution. The reason for introducing a new "builtin" as opposed to utilizing a trap is because safely appending to a trap can be filled with holes. Since trap always overwrites what is in the trap, you have to be aware of what is already in the trap, and ensure you are properly appending to it (and that a previous error in the trap processing doesn't affect you). This removes the nice benefit of multi-step setups, so for example if we rewrite the scoped error mode with traps we get:
``` scoped_error_mode() { if ! echo -n "$SHELLOPTS" | grep 'errexit' >/dev/null 2>&1; then echo "error mode off, enabling for this function" set -e trap "set +e" EXIT_LOCAL RETURN_LOCAL fi if ! echo -n "$SHELLOPTS" | grep 'pipefail' >/dev/null 2>&1; then echo "pipefail off, enabling for this function" set -o pipefail if [ "x$(trap -p EXIT_LOCAL)" != "x" ]; then trap "$(trap -p EXIT_LOCAL) ; set +o pipefail" # if dealing with quotes have to sed them out fi if [ "x$(trap -p RETURN_LOCAL)" != "x" ]; then trap "$(trap -p RETURN_LOCAL); set +o pipefail" fi fi my_commands my_other_commands | piped-to } ``` This isn't terrible by any means, and is "more in line" with the existing practices. I still think defer might be more simple, but that is just my opinion! I think both are totally workable. - Cynthia On Thu, Oct 6, 2022 at 4:05 PM Lawrence Velázquez <v...@larryv.me> wrote: > > On Thu, Oct 6, 2022, at 4:08 PM, Cynthia Coan wrote: > > I'd specifically like to propose a new built-in called `defer` which > > acts like `eval` however is not parsed/expanded/run until it's scope > > is leaving. Hopefully "scope" is the correct word, I'm imagining it > > running at the same time a local would go out of "scope" and be no > > longer available (just before the locals are cleared, so locals can > > still be used in expansion). > > I think it would be more natural to implement function-local RETURN > and EXIT traps than introduce a second command that looks like > 'trap' and quacks like 'trap' but is actually not 'trap'. This > could be done generically by adding the ability to "scope" traps > to functions (possibly via a new option to 'trap' or a shopt à la > zsh's LOCAL_TRAPS) or specifically by creating "local" variants of > RETURN and EXIT. Usage might look like this: > > f() { > # new option > trap -f 'cleaning up' EXIT RETURN > cmd1 > cmd2 > cmd3 > } > > or this: > > g() { > # new traps > trap 'cleaning up' EXIT_LOCAL RETURN_LOCAL > cmdA > cmdB > cmdC > } > > -- > vq