
У меня есть функция, и я хотел бы использовать опцию pipefail
внутри. Но я не хочу просто set -o pipefail
, потому что боюсь, что другая часть скрипта может не ожидать pipefail
установки. Конечно, я мог бы сделать это set +o pipefail
позже, но в случае pipefail
установки вне моей функции это также может привести к неожиданному поведению.
Конечно, я мог бы использовать код выхода false | true
в качестве меры, если pipefail
он установлен, но это кажется немного грязным.
Есть ли более общий (и, может быть, канонический?) способ проверить установленные параметры bash?
решение1
В bash-4.4
и выше вы можете использовать:
myfunction() {
local -
set -o someoption
...
}
Несколько заметок:
- скопировано из
ash
. Вash
, идея состоит в том, чтобы сделать$-
(который хранит набор опций) локальным. Это работает в ,ash
потому что вash
,local
не влияет на значение или атрибуты переменной (в отличие отbash
того, где он изначально делает ее неустановленной). Тем не менее, это также работает сbash
тем же способом, что и вash
как особый случай, то есть это не приводит$-
к неустановленности или возврату к поведению по умолчанию - он охватывает только параметры, заданные с помощью
set -o option
, а не те, которые заданыshopt -s option
(bash
будучи единственной оболочкой с двумя наборами параметров!) как и для локальных переменных, это динамическая область видимости, а не статическая. Значение
$-
будет восстановлено при возврате из функции, но новая настройка повлияет на другие функции или исходные скрипты, вызываемые вашей функцией. Если вам нужна статическая область видимости, вам нужно будет переключиться на ksh93 и использовать:function myfunction { set -o myoption ... other_function }
На это
other_function
не влияет, если только он объявлен сfunction other_function {
синтаксисом (а не с синтаксисом Борнаother_function()...
).Поскольку он не сбрасывает параметры, ваша функция все еще может быть затронута параметрами, установленными другим кодом. Чтобы избежать этого, вы бы хотели использовать
zsh
вместо этого.zsh
эквивалент islocal -
,set -o localoptions
но вы также можете получить хорошо известныйэмуляциярежимлокально. Например:myfunction() { emulate -L zsh set -o someoption ... }
начнется с разумного набора опций zsh-default (с некоторыми исключениями, см. документацию), поверх которого вы добавляете свою опцию. Это также динамическое определение области действия, как в ash/bash, но вы также можете вызывать другие функции с помощью:
emulate zsh -c 'other_function...'
для того, чтобы его
other_function
можно было вызвать с установленным набором опций vanilla zsh.zsh также имеет ряд операторов, которые могут помочь вам избежать изменения опций в глобальном масштабе в первую очередь (например,
(N)
/(D)
glob-квалификаторы дляnullglob
/dotglob
,(#i)
glob-оператор для подстановки без учета регистра,${(s:x:)var}
чтобы избежать смешивания с$IFS
,${~var}
чтобызапросподстановка при расширении параметров (вам нужноnoglob
в других оболочках типа Bourneизбегать(!) это)...
решение2
Переменная $SHELLOPTS
содержит все заданные параметры, разделенные двоеточиями. Например, это может выглядеть так:
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
Для проверки заданной опции можно сделать следующее:
# check if pipefail is set, if not set it and remember this
if [[ ! "$SHELLOPTS" =~ "pipefail" ]]; then
set -o pipefail;
PIPEFAIL=0;
else
PIPEFAIL=1;
fi
# do something here
# reset pipefail, if it was not set before
if [ "$PIPEFAIL" -eq 0 ]; then
set +o pipefail;
fi
решение3
Если вы хотите установить параметр оболочки внутри функции, но не хотите влиять на вызывающую функцию, вы можете сделать это двумя способами:
Вариант 1: подоболочка
Поместите функцию внутрь подоболочки (обратите внимание на круглые скобки, окружающие операторы функции, а не фигурные скобки):
function foo() (
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
)
Тогда вызывающая сторона может отключить pipefail и это не повлияет на нее:
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
или
Вариант 2: тест-сброс
При необходимости можно протестировать и сбросить параметр (обратите внимание на фигурные скобки вокруг операторов функций — подоболочки нет):
function foo() {
shopt -q -o pipefail
local resetpipefail=$?
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
# only reset pipefail if needed
[[ $resetpipefail -eq 1 ]] && set +o pipefail
}
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
Снимаю шляпукуонглмдля ихshopt -q
отвечать.
решение4
Используйте ловушку RETURN
Команды, указанные с помощью
RETURN
прерывания, выполняются до возобновления выполнения после выполнения функции оболочки или сценария оболочки с помощью.
илиsource
возвращаются.[...]
Параметр-p
[to shopt] позволяет отображать выходные данные в форме, которую можно повторно использовать в качестве входных данных.
foo() {
trap "$(shopt -p -o pipefail)" RETURN
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
}
Тест
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
Следующий шаблон функции обрабатывает общий случай, гарантируя, что оба параметра set
и shopt
восстанавливаются до своих исходных значений при возврате функции:
foo() {
trap "$(shopt -p -o; shopt -p)" RETURN # ①
# ...
}
① shopt -p -o
можно упростить до стандартного эквивалента set +o
.