Quero set -x
no início do meu script e "desfazer" (voltar ao estado antes de defini-lo) depois, em vez de definir cegamente +x
. Isso é possível?
PS: Eu já verifiquei aqui ; isso não parecia responder à minha pergunta até onde eu sabia.
Quero set -x
no início do meu script e "desfazer" (voltar ao estado antes de defini-lo) depois, em vez de definir cegamente +x
. Isso é possível?
PS: Eu já verifiquei aqui ; isso não parecia responder à minha pergunta até onde eu sabia.
Respostas:
Para reverter um set -x
basta executar a set +x
. Na maioria das vezes, o inverso de uma string set -str
é a mesma seqüência com um +
: set +str
.
Em geral, para restaurar todas as errexit
opções de shell (leia abaixo sobre o bash ) (alteradas com o set
comando), você pode fazer (também leia abaixo sobre as shopt
opções do bash ):
oldstate="$(set +o); set -$-" # POSIXly store all set options.
.
.
set -vx; eval "$oldstate" # restore all options stored.
Este comando:
shopt -po xtrace
irá gerar uma cadeia de caracteres executável que reflete o estado da opção. O p
sinalizador significa imprimir, e o o
sinalizador especifica que estamos perguntando sobre as opções definidas pelo set
comando (em oposição às opções definidas pelo shopt
comando). Você pode atribuir essa sequência a uma variável e executar a variável no final do seu script para restaurar o estado inicial.
# store state of xtrace option.
tracestate="$(shopt -po xtrace)"
# change xtrace as needed
echo "some commands with xtrace as externally selected"
set -x
echo "some commands with xtrace set"
# restore the value of xtrace to its original value.
eval "$tracestate"
Esta solução funciona para várias opções simultaneamente:
oldstate="$(shopt -po xtrace noglob errexit)"
# change options as needed
set -x
set +x
set -f
set -e
set -x
# restore to recorded state:
set +vx; eval "$oldstate"
A adição set +vx
evita a impressão de uma longa lista de opções.
E, se você não listar nenhum nome de opção,
oldstate="$(shopt -po)"
fornece os valores de todas as opções. E, se você deixar de fora a o
bandeira, poderá fazer o mesmo com as shopt
opções:
# store state of dotglob option.
dglobstate="$(shopt -p dotglob)"
# store state of all options.
oldstate="$(shopt -p)"
Se você precisar testar se uma set
opção está definida, a maneira mais idiomática (Bash) de fazer isso é:
[[ -o xtrace ]]
o que é melhor que os outros dois testes semelhantes:
[[ $- =~ x ]]
[[ $- == *x* ]]
Com qualquer um dos testes, isso funciona:
# record the state of the xtrace option in ts (tracestate):
[ -o xtrace ] && ts='set -x' || ts='set +x'
# change xtrace as needed
echo "some commands with xtrace as externally selected"
set -x
echo "some commands with xtrace set"
# set the xtrace option back to what it was.
eval "$ts"
Veja como testar o estado de uma shopt
opção:
if shopt -q dotglob
then
# dotglob is set, so “echo .* *” would list the dot files twice.
echo *
else
# dotglob is not set. Warning: the below will list “.” and “..”.
echo .* *
fi
Uma solução simples e compatível com POSIX para armazenar todas as set
opções é:
set +o
que é descrito no padrão POSIX como:
+ o
Escreva as configurações de opções atuais na saída padrão em um formato adequado para reinicialização no shell como comandos que atingem as mesmas configurações de opções.
Então, simplesmente:
oldstate=$(set +o)
preservará valores para todas as opções definidas usando o set
comando
Novamente, restaurar as opções para seus valores originais é uma questão de executar a variável:
set +vx; eval "$oldstate"
Isso é exatamente equivalente ao uso do Bash's shopt -po
. Observe que ele não cobrirá todas as opções possíveis do Bash , pois algumas delas são definidas por shopt
.
Há muitas outras opções do shell listados com shopt
no bash:
$ shopt
autocd off
cdable_vars off
cdspell off
checkhash off
checkjobs off
checkwinsize on
cmdhist on
compat31 off
compat32 off
compat40 off
compat41 off
compat42 off
compat43 off
complete_fullquote on
direxpand off
dirspell off
dotglob off
execfail off
expand_aliases on
extdebug off
extglob off
extquote on
failglob off
force_fignore on
globasciiranges off
globstar on
gnu_errfmt off
histappend on
histreedit off
histverify on
hostcomplete on
huponexit off
inherit_errexit off
interactive_comments on
lastpipe on
lithist off
login_shell off
mailwarn off
no_empty_cmd_completion off
nocaseglob off
nocasematch off
nullglob off
progcomp on
promptvars on
restricted_shell off
shift_verbose off
sourcepath on
xpg_echo off
Eles podem ser anexados à variável definida acima e restaurados da mesma maneira:
$ oldstate="$oldstate;$(shopt -p)"
.
. # change options as needed.
.
$ eval "$oldstate"
É possível fazer (o $-
é anexado para garantir a errexit
preservação):
oldstate="$(shopt -po; shopt -p); set -$-"
set +vx; eval "$oldstate" # use to restore all options.
Nota : cada shell possui uma maneira ligeiramente diferente de criar a lista de opções configuradas ou não (para não mencionar as opções diferentes definidas), portanto, as strings não são portáveis entre shells, mas são válidas para o mesmo shell.
zsh
também funciona corretamente (após o POSIX) desde a versão 5.3. Nas versões anteriores, seguia o POSIX apenas parcialmente, set +o
na medida em que imprimia opções em um formato adequado para reinicializar o shell como comandos, mas apenas para opções definidas (não imprimia opções não definidas ).
O mksh (e, consequentemente, lksh) ainda não é capaz (MIRBSD KSH R54 2016/11/11). O manual mksh contém:
Em uma versão futura, set + o comportará comandos compatíveis com POSIX e imprimirá para restaurar as opções atuais.
No bash, o valor de set -e
( errexit
) é redefinido dentro de subcascas, o que dificulta a captura de seu valor set +o
dentro de uma subcasca $ (…).
Como solução alternativa, use:
oldstate="$(set +o); set -$-"
Com o shell Almquist e seus derivados ( pelo menos dash
NetBSD / FreeBSD sh
) e bash
4.4 ou superior, você pode tornar as opções locais para uma função com local -
(torne a $-
variável local se desejar):
$ bash-4.4 -c 'f() { local -; set -x; echo test; }; f; echo no trace'
+ echo test
test
no trace
Isso não se aplica a origem arquivos, mas você pode redefinir source
como source() { . "$@"; }
para trabalho em torno disso.
Com ksh88
, as alterações de opção são locais para a função por padrão. Com ksh93
, esse é apenas o caso das funções definidas com a function f { ...; }
sintaxe (e o escopo é estático comparado ao escopo dinâmico usado em outros shells, incluindo o ksh88):
$ ksh93 -c 'function f { set -x; echo test; }; f; echo no trace'
+ echo test
test
no trace
Em zsh
, isso é feito com a localoptions
opção:
$ zsh -c 'f() { set -o localoptions; set -x; echo test; }; f; echo no trace'
+f:0> echo test
test
no trace
POSIXly, você pode fazer:
case $- in
(*x*) restore=;;
(*) restore='set +x'; set -x
esac
echo test
{ eval "$restore";} 2> /dev/null
echo no trace
No entanto, alguns shells produzirão a + 2> /dev/null
após a restauração (e você verá o rastreio dessa case
construção, é claro, se set -x
já estiver ativado). Essa abordagem também não é reentrada (como se você fizer isso em uma função que se chama ou outra função que usa o mesmo truque).
Consulte https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh (escopo local para variáveis e opções para shells POSIX) para saber como implementar uma pilha que contorna isso.
Com qualquer shell, você pode usar subshells para limitar o escopo das opções
$ sh -c 'f() (set -x; echo test); f; echo no trace'
+ echo test
test
no trace
No entanto, isso limita o escopo de tudo (variáveis, funções, aliases, redirecionamentos, diretório de trabalho atual ...), não apenas opções.
oldstate=$(set +o)
é uma maneira mais simples (e POSIX) de armazenar todas as opções.
pdksh
/ mksh
(e outros derivados pdksh) ou zsh
onde set +o
apenas gera os desvios das configurações padrão. Isso funcionaria para o bash / dash / yash, mas não seria portátil.
Você pode ler a $-
variável no início para ver se -x
está definida ou não e depois salvá-la em uma variável, por exemplo
if [[ $- == *x* ]]; then
was_x_set=1
else
was_x_set=0
fi
($ -, um hífen.) Expande para os sinalizadores de opções atuais, conforme especificado na chamada, pelo comando set builtin ou pelos definidos pelo próprio shell (como a opção -i).
[[ $SHELLOPTS =~ xtrace ]] && wasset=1
set -x
echo rest of your script
[[ $wasset -eq 0 ]] && set +x
No bash, $ SHELLOPTS é definido com os sinalizadores ativados. Verifique-o antes de ativar o xtrace e redefina o xtrace apenas se estiver desativado antes.
Apenas para declarar o óbvio, se set -x
deve ter efeito durante a duração do script, e esta é apenas uma medida de teste temporária (para não fazer parte permanentemente da saída), invoque o script com a -x
opção, por exemplo,
$ bash -x path_to_script.sh
... ou, altere temporariamente o script (primeira linha) para ativar a saída de depuração adicionando a -x
opção:
#!/bin/bash -x
...rest of script...
Sei que isso provavelmente é um golpe muito amplo para o que você deseja, mas é a maneira mais simples e rápida de ativar / desativar, sem complicar demais o script com coisas temporárias que você provavelmente deseja remover de qualquer maneira (na minha experiência).
Isso fornece funções para salvar e restaurar os sinalizadores visíveis através do $-
parâmetro especial POSIX . Usamos a local
extensão para variáveis locais. Em um script portátil em conformidade com POSIX, variáveis globais seriam usadas (sem local
palavra-chave):
save_opts()
{
echo $-
}
restore_opts()
{
local saved=$1
local on
local off=$-
while [ ${#saved} -gt 0 ] ; do
local rest=${saved#?}
local first=${saved%$rest}
if echo $off | grep -q $first ; then
off=$(echo $off | tr -d $first)
fi
on="$on$first"
saved=$rest
done
set ${on+"-$on"} ${off+"+$off"}
}
Isso é usado de maneira semelhante à maneira como os sinalizadores de interrupção são salvos e restaurados no kernel do Linux:
Shell: Kernel:
flags=$(save_opts) long flags;
save_flags (flags);
set -x # or any other local_irq_disable(); /* disable irqs on this core */
# ... -x enabled ... /* ... interrupts disabled ... */
restore_opts $flags restore_flags(flags);
# ... x restored ... /* ... interrupts restored ... */
Isso não funcionará para nenhuma das opções estendidas que não são cobertas na $-
variável.
Acabei de notar que o POSIX tem o que eu estava procurando: o +o
argumento de set
não é uma opção, mas um comando que despeja um monte de comandos que, se eval
-ed, restauram as opções. Assim:
flags=$(set +o)
set -x
# ...
eval "$flags"
É isso aí.
Um pequeno problema é que, se a -x
opção for ativada antes disso eval
, um feio fluxo de set -o
comandos é visto. Para eliminar isso, podemos fazer o seguinte:
set +x # turn off trace not to see the flurry of set -o.
eval "$flags" # restore flags
Você pode usar um sub-shell.
(
set …
do stuff
)
Other stuff, that set does not apply to