bash: escapa linhas individuais do eco `-x`

11

No bash, ao executar com a -xopção, é possível isentar comandos individuais do eco?

Estou tentando tornar a saída o mais organizada possível, por isso estou executando certas partes do meu script em um subshell com set +x. No entanto, a set +xprópria linha ainda é ecoada e não adiciona informações valiosas à saída.

Lembro-me de antigamente .bat, quando corríamos com echo on, linhas individuais podiam ser isentas iniciando-as com a @. Existe algum equivalente no bash?

#!/bin/bash -x

function i_know_what_this_does() {
  (
    set +x
    echo do stuff
  )
}

echo the next-next line still echoes 'set +x', is that avoidable?
i_know_what_this_does
echo and we are back and echoing is back on

Ao executar o acima, a saída é:

+ echo the next-next line still echoes 'set +x,' is that 'avoidable?'
the next-next line still echoes set +x, is that avoidable?
+ i_know_what_this_does
+ set +x
do stuff
+ echo and we are back and echoing is back on
and we are back and echoing is back on
clacke
fonte

Respostas:

22

xtracesaída vai para stderr, então você pode redirecionar stderrpara /dev/null:

i_know_what_this_does() {
  echo do stuff
} 2> /dev/null

Se você ainda deseja ver os erros dos comandos executados dentro das funções, você pode fazer

i_know_what_this_does() (
  { set +x; } 2> /dev/null # silently disable xtrace
  echo do stuff
)

Observe o uso de, em (...)vez de, {...}para fornecer um escopo local para essa função por meio de um subshell. bash, como a versão 4.4 agora suporta local -como no shell Almquist para tornar as opções locais para a função (semelhante a set -o localoptionsin zsh), você pode evitar o subshell fazendo o seguinte:

i_know_what_this_does() {
  { local -; set +x; } 2> /dev/null # silently disable xtrace
  echo do stuff
}

Uma alternativa para bash4.0 a 4.3 seria usar a $BASH_XTRACEFDvariável e ter um descritor de arquivo dedicado aberto /dev/nullpara isso:

exec 9> /dev/null
set -x
i_know_what_this_does() {
  { local BASH_XTRACEFD=9; } 2> /dev/null # silently disable xtrace
  echo do stuff
}

Como bashfalta a capacidade de marcar um fd com o sinalizador close-on-exec , isso tem o efeito colateral de vazar esse fd para outros comandos.

Veja também este locvar.sh, que contém algumas funções para implementar o escopo local de variáveis ​​e funções nos scripts POSIX e também fornece trace_fne untrace_fnfunções para torná-las xtrace d ou não.

Stéphane Chazelas
fonte
Doce! Eu estava olhando para ver se havia algum modificador que eu pudesse aplicar à função em si, mas não pensei em simplesmente redirecionar o stderr. Obrigado!
clacke
1
Btw, stchaz.free.fr/which_interpreter da mesma página é bastante impressionante e perturbador. :-)
clacke 14/01
E agora voltei aqui novamente para o segundo método, silenciando o conjunto + x sem silenciar a saída stderr útil. Obrigado novamente!
clacke
2

A razão set +ximpressa é que set -xsignifica "imprima o comando que você está prestes a executar, com expansões, antes de executá-lo . Portanto, o shell não sabe que você deseja que ele não imprima coisas até depois de imprimir a linha, dizendo que não para imprimir as coisas.De acordo com o meu conhecimento, não há como impedir que isso aconteça.

Jenny D
fonte
0

Aqui está a solução que você está procurando:

function xtrace() {
  # Print the line as if xtrace was turned on, using perl to filter out
  # the extra colon character and the following "set +x" line.
  (
    set -x
    # Colon is a no-op in bash, so nothing will execute.
    : "$@"
    set +x
  ) 2>&1 | perl -ne 's/^[+] :/+/ and print' 1>&2
  # Execute the original line unmolested
  "$@"
}

O comando original é executado no mesmo shell em uma transformação de identidade. Antes da execução, você obtém um xtrace não recursivo dos argumentos. Isso permite rastrear os comandos de que você gosta sem enviar spam ao stederr com cópias duplicadas de cada comando "eco".

# Example
echo "About to do something complicated ..."
xtrace do_something_complicated
Dave Dopson
fonte
Ou para evitar perl(e problemas com comandos de várias linhas):+() { :;} 2> /dev/null; xtrace() { (PS4=; set -x; + "$@";{ set +x; } 2> /dev/null); "$@";}
Stéphane Chazelas
Não, desculpe, unix.stackexchange.com/a/60049/17980 é a solução que eu estava procurando. :-) As set -xmanobras me compram algo comparado a apenas printf >&2 '+ %s\n' "$*"?
Clacke 01/09/2015
Como em:xtrace() { printf >&2 '+ %s\n' "$*"; "$@"; }
clacke 01/09/2015