
Tenho uma função e gostaria de usar a pipefail
opção dentro dela. Mas não quero simplesmente fazê-lo set -o pipefail
, porque temo que outra parte do roteiro não possa pipefail
ser definida. É claro que eu poderia fazer isso set +o pipefail
depois, mas caso pipefail
tenha sido definido fora da minha função, isso também pode apresentar um comportamento inesperado.
É claro que eu poderia usar o código de saída false | true
como medida, se pipefail
estiver definido, mas isso parece um pouco sujo.
Existe uma maneira mais geral (e talvez canônica?) De verificar as opções definidas do bash?
Responder1
Acima bash-4.4
e acima, você pode usar:
myfunction() {
local -
set -o someoption
...
}
Algumas notas:
- que foi copiado de
ash
. Noash
, a ideia é tornar$-
(que armazena o conjunto de opções) local. Funcionaash
porque inash
,local
não afeta o valor nem os atributos da variável (ao contrário debash
onde inicialmente a desativa). Ainda assim, também funciona dabash
mesma maneira que emash
um caso especial, ou seja, não causa$-
a desativação ou a reversão para um comportamento padrão - cobre apenas as opções definidas com
set -o option
, não as definidas porshopt -s option
(bash
sendo o único shell com dois conjuntos de opções!) como para variáveis locais, é um escopo dinâmico, não estático. O valor de
$-
será restaurado ao retornar da função, mas a nova configuração afetará as outras funções ou scripts de origem chamados pela sua função. Se quiser um escopo estático, você precisará mudar para ksh93 e usar:function myfunction { set -o myoption ... other_function }
Com o
other_function
não afetado, desde que seja declaradofunction other_function {
também com a sintaxe (e não com aother_function()...
sintaxe Bourne).Como não redefine as opções, sua função ainda poderá ser afetada por opções definidas por outro código. Para evitar isso, você gostaria de usar
zsh
.zsh
é equivalente alocal -
isset -o localoptions
, mas você também pode chegar a um conhecidoemulaçãomodolocalmente. Por exemplo:myfunction() { emulate -L zsh set -o someoption ... }
começaria com um conjunto sensato de opções zsh-default (com algumas exceções, consulte o documento) sobre o qual você adicionaria sua opção. Isso também é escopo dinâmico como no ash/bash, mas você também pode chamar outras funções com:
emulate zsh -c 'other_function...'
para que isso
other_function
seja chamado com um conjunto de opções vanilla zsh.zsh também possui vários operadores que podem fazer com que você evite alterar opções globalmente em primeiro lugar (como
(N)
/(D)
qualificadores glob paranullglob
/dotglob
,(#i)
operador glob para globbing sem distinção entre maiúsculas e minúsculas,${(s:x:)var}
para evitar mistura com$IFS
,${~var}
parasolicitarglobbing na expansão de parâmetros (você precisanoglob
em outros shells do tipo Bourne paraevitar(!) isto)...
Responder2
A $SHELLOPTS
variável contém todas as opções definidas separadas por dois pontos. Por exemplo, pode parecer:
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
Para verificar uma determinada opção, pode-se fazer o seguinte:
# 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
Responder3
Se você deseja definir uma opção de shell dentro de uma função, mas não deseja afetar o chamador, faça isso de duas maneiras diferentes:
Opção 1: subshell
Coloque a função dentro de um subshell (observe os parênteses que cercam as instruções da função em vez das chaves):
function foo() (
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
)
Então, o chamador pode ter o pipefail desativado e não será afetado:
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
ou
Opção 2: testar e redefinir
Você pode testar e redefinir a opção conforme necessário (observe as chaves ao redor das instruções de função - sem subshell):
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
Gorjeta de chapéu paracuonglmpare elesshopt -q
responder.
Responder4
Use uma armadilha RETURN
Os comandos especificados com um
RETURN
trap são executados antes da execução ser retomada após uma função shell ou um script shell executado com.
ousource
retornar.[...]
A-p
opção [comprar] faz com que a saída seja exibida em um formato que pode ser reutilizado como entrada.
foo() {
trap "$(shopt -p -o pipefail)" RETURN
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
}
Teste
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
O modelo de função a seguir trata do caso geral de garantir que ambas as opções set
e shopt
sejam restauradas aos seus valores originais quando uma função retorna:
foo() {
trap "$(shopt -p -o; shopt -p)" RETURN # ①
# ...
}
① shopt -p -o
pode ser simplificado para o equivalente padrão set +o
.