Skip to content

Instantly share code, notes, and snippets.

@slowpeek
Last active August 20, 2021 15:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slowpeek/3e592af3395cfb0e2d20a57c50a87a4b to your computer and use it in GitHub Desktop.
Save slowpeek/3e592af3395cfb0e2d20a57c50a87a4b to your computer and use it in GitHub Desktop.
shopt_ensure
# -*- mode: sh; sh-shell: bash; -*-
# shellcheck shell=bash
# MIT license (c) 2021 https://github.com/slowpeek
# Homepage: https://gist.github.com/slowpeek/3e592af3395cfb0e2d20a57c50a87a4b
# bye.sh https://gist.github.com/slowpeek/6127166369d8abd230c30c20cc6a9152
# stack.sh https://gist.github.com/slowpeek/3310c2f09b80d0b2e175122e8c8cae64
# This is a wrapper around bash builtin shopt. It can be used to save
# the current state for some options, set/unset those and recover the
# original state later.
#
# shopt_ensure is like shopt, it uses -s/-u switches to set/unset
# options. Unlike shopt, it accepts both in a single call:
#
# shopt_ensure -s opt1 opt2 -u opt3
#
# -s/-u can be mixed any way:
#
# shopt_ensure -s opt1 opt2 -u opt3 opt4
# shopt_ensure -s opt1 -s opt2 -u opt3 -u opt4
# shopt_ensure -u opt1 -s opt2 -u opt3 -s opt4
#
# By default there is implicit -s: 'shopt_ensure opt' is the same as
# 'shopt_ensure -s opt'.
#
# On shopt_ensure call the original state of the options listed as
# args is pushed into a stack as a single element. shopt_ensure_undo
# pops off an element and uses it to restore the saved state.
#
# EXAMPLE
#
# shopt_ensure -s globstar nullglob
# printf '%s\n' **
# shopt_ensure_undo
_ () {
unset -f _
declare -g -A SE__NAMES=() # Valid option names.
local opt
while read -r opt; do
SE__NAMES[${opt%%[[:space:]]*}]=t
done < <(shopt; shopt -o)
}; _
shopt_ensure () {
local mode=s state
while (($# > 0)); do
if [[ $1 == -[su] ]]; then
mode=${1:1}
shift
continue
fi
[[ -v SE__NAMES[$1] ]] || bye "Unknown option ${1@Q}"
if shopt -q "$1"; then
state+=" s $1"
[[ $mode == s ]] || shopt -u "$1"
else
state+=" u $1"
[[ $mode == u ]] || shopt -s "$1"
fi
shift
done
[[ -n ${state-} ]] || bye 'Empty options list.'
push_n shopt_ensure state
}
shopt_ensure_undo () {
local state
pop_n shopt_ensure state
# shellcheck disable=SC2086
set -- $state
while (($# > 0)); do
shopt -"$1" "$2"
shift 2
done
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment