Bash definido + x sem ser impresso

91

Alguém sabe se podemos dizer set +xem bash sem que seja impresso:

set -x
command
set +x

vestígios

+ command
+ set +x

mas deve apenas imprimir

+ command

O Bash é a versão 4.1.10 (4). Isso está me incomodando há algum tempo - a saída está cheia de set +xlinhas inúteis , tornando o rastreio não tão útil quanto poderia ser.

Andreas Spindler
fonte
Isso não responde à sua pergunta, mas quando você executa o seu script, por que não:script.sh 2>&1 | grep -v 'set +x'
cdarke

Respostas:

144

Eu tive o mesmo problema e consegui encontrar uma solução que não usa um subshell:

set -x
command
{ set +x; } 2>/dev/null
McJoey
fonte
10
Ótima resposta, apenas uma observação: sem um ponto-e-vírgula após o comando isso não funcionará; e com um ponto e vírgula, mas sem espaços entre colchetes, um erro de sintaxe será gerado.
sdaau
9
Isso zera o status de saída.
Garth Kidd
8
@GarthKidd O status de saída é zerado a cada comando bem-sucedido. set +xé um comando tão bem-sucedido
Daniel Alder
3
Nos casos em que é necessário o status de saída command, essa variação é uma solução: { STATUS=$?; set +x; } 2>/dev/null. Em seguida, inspecione $STATUSnas linhas subsequentes em seu lazer.
Greg Price
5
Separadamente, aqui está uma versão golfed um pouco mais: { set +x; } 2>&-. Isso fecha fd 2 completamente em vez de apontar para / dev / null. Alguns programas não lidam bem com isso quando tentam imprimir em stderr, e é por isso que / dev / null é um bom estilo em geral; mas o set -xrastreamento da concha lida com isso muito bem, então funciona perfeitamente aqui, e torna este encantamento um pouco mais curto.
Greg Price
43

Você pode usar um subshell. Ao sair do subshell, a configuração para xserá perdida:

( set -x ; command )
choroba
fonte
Bem, obrigado ... bom ponto. Na verdade, estou ciente do "truque da sub-casca". Eu esperava que pudesse ser mais fácil do que isso. Envolve mudanças substanciais no código, tornando-o mais complexo e menos legível. IMHO isso seria pior do que viver com o conjunto + x linhas ...
Andreas Spindler
Não vejo como ( set -x \n command \n )é pior do que set -x \n command \n set +x.
chepner
3
@chepner: Você não pode definir variáveis.
choroba
2
... e você não pode cd: não muda o diretório atual no shell pai.
Andreas Spindler
Não tenho certeza de qual foi sua objeção real. Tem sido uma longa semana ...
chepner
8

Eu criei uma solução para isso recentemente, quando fiquei irritado com isso:

shopt -s expand_aliases
_xtrace() {
    case $1 in
        on) set -x ;;
        off) set +x ;;
    esac
}
alias xtrace='{ _xtrace $(cat); } 2>/dev/null <<<'

Isso permite que você habilite e desabilite o xtrace como a seguir, onde estou registrando como os argumentos são atribuídos às variáveis:

xtrace on
ARG1=$1
ARG2=$2
xtrace off

E você obtém uma saída semelhante a:

$ ./script.sh one two
+ ARG1=one
+ ARG2=two
user108471
fonte
Truque inteligente (embora você não precise da /dev/stdinparte). A ressalva é que ativar a expansão de alias em scripts pode ter efeitos colaterais indesejados.
mklement0
Você está certo. Editei a resposta para remover o supérfluo /dev/stdin. Não estou ciente de nenhum efeito colateral específico, uma vez que o ambiente não interativo não deve carregar nenhum arquivo que defina aliases. Que efeitos colaterais podem ocorrer?
user108471
1
Esse é um bom ponto - esqueci que os aliases não são herdados, então o risco é muito menor do que eu pensava (hipoteticamente, seu script pode estar fornecendo código de terceiros que por acaso define os aliases, mas concordo que provavelmente não é real preocupação mundial). +1
mklement0
1
@AndreasSpindler Você poderia explicar por que acha que essa técnica tem maior probabilidade de vazar informações confidenciais?
user108471
1
... basicamente porque aliases e funções são construções de alto nível. set +xé muito mais difícil (senão impossível) de se comprometer. Mas ainda é uma solução boa e direta - provavelmente a melhor até agora.
Andreas Spindler
7

Que tal uma solução baseada em uma versão simplificada de @ user108471:

shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ set +x; } 2>/dev/null'

trace_on
...stuff...
trace_off
Oliver
fonte
Que tal agora? function () { set_plus_x='{ set +x; } 2>/dev/null' }
LexH
1

Esta é uma combinação de algumas idéias que podem incluir um bloco de código e preserva o status de saída.

#!/bin/bash
shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ PREV_STATUS=$? ; set +x; } 2>/dev/null; (exit $PREV_STATUS)'

trace_on
echo hello
trace_off
echo "status: $?"

trace_on
(exit 56)
trace_off

Quando executado:

$ ./test.sh 
+ echo hello
hello
status: 0
+ exit 56
status: 56
user3286792
fonte
Bom esforço, mas eu acho que a ()volta exitnão é necessário. Está bem. Talvez isso é paranóico, mas se este código é utilizado, em geral, você tem um vector bom ataque: redefine trace_one trace_offe código de injetar que lê comandos executados. Se você usar esses "utilitários" sozinhos, será instrutivo, mas se o código for usado com outros, você deve considerar se os benefícios de tais funções não padronizadas superam as desvantagens. Pessoalmente, eu me { set +x; } 2>/dev/nullconformei com uma vez que essa construção é comumente entendida e também não altera o status de saída.
Andreas Spindler