Значение дополнительных скобок заключается в том, что они инициализируют массивы в Bash. Это не имеет большого значения при обработке вывода uname -r
, состоящего из одного слова, но дляCOMPREPLY
это важно.
Можете убедиться в этом сами:
var1=($(uname -a))
echo $var1
echo ${var1[@]}
Да, возможно:
fun(){ local -
# store state of all options.
oldstate="$(shopt -p)"
:
:
set +vx; eval "$oldstate"
}
Существует особый случай с errexit
.
Я думаю, что нашел решение, которое соответствует моим потребностям, выполнив тело функции в подоболочке . Преимущества и недостатки выполнения тела функции в подоболочке, а также типичные варианты использования обсуждались здесь много раз [1] [2] [3] .
Как указано в [4] , взятом со страницы bash man
, общий синтаксис функции
[ function ] name () compound-command [redirection]
A Compound comamand
может быть дочерним процессом текущей оболочки, который инициируется с помощью синтаксиса круглых скобок (list)
. man bash
дает следующее определение (выделение собственным шрифтом):
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes. The return status is the exit status of list.
В моем случае нам нужны две вещи:
shopt
extglob
. Из-за изменений в подоболочке shopt
параметр extglob
не будет просачиваться в окружающую оболочку. Кроме того, мы можем вернуться в любой момент из функции. Отрицательным моментом является, конечно, то, что запуск подшелла потребует дополнительных ресурсов.
Демонстрация
Теперь гораздо более короткие функции выглядят так:
yesnoquery () (
while true; do
if read -p "$1" answer; then
shopt -s extglob
eval '
case "$answer" in
@([Yy]|[Yy][Ee][Ss]) ) return 0;; #true
@([Nn]|[Nn][Oo]) ) return 1;; #false
* ) echo "Please answer \"yes\" or \"no\"";;
esac
'
else
return 2 #reading returned error
fi
done
)
В операторе case мы теперь можем напрямую выполнять возврат вместо сохранения во временную переменную.Локализованные переменные больше не нужны, и два условия были сохранены.
То, что параметры shopt
действительно являются локальными, можно быстро увидеть на примере кода из кода в вопросе
#!/bin/bash
# Enable the options globally
set -u
[[ "$SHELLOPTS" =~ nounset ]] && echo "1: nounset enabled"
shopt -s "extglob"
[[ "$BASHOPTS" =~ extglob ]] && echo "1: extglob enabled"
fun () {
set +u
[[ "$SHELLOPTS" =~ nounset ]] || echo "2: nounset disabled"
shopt -u "extglob"
[[ "$BASHOPTS" =~ extglob ]] || echo "2: extglob disabled"
}
fun
if [[ "$SHELLOPTS" =~ nounset ]]; then
echo "3: nounset enabled"
else
echo "3: nounset disabled"
fi
if [[ "$BASHOPTS" =~ extglob ]]; then
echo "3: extglob enabled"
else
echo "3: extglob disabled"
fi
Все, что было изменено, это скобки здесь (
вместо{
(и удалены лишние локальные переменные ). Выход
1: nounset enabled
1: extglob enabled
2: nounset disabled
2: extglob disabled
3: nounset enabled
3: extglob enabled
показывает, что эффекты были успешно локализованы.
Резюме
Среда выполнения подоболочки как тело функции оказалась полезной для локализации эффектов опций оболочки bash. Операторы case
в теле функции могут извлечь выгоду из extglob
, так как изменения этой переменной не повлияют на код вне области действия функции. Если ресурсов не хватает, дополнительные накладные расходы на запуск подоболочки могут излишне ухудшить производительность.