Suprimir o rastreamento de execução do comando echo?

Suprimir o rastreamento de execução do comando echo?

Estou executando scripts de shell do Jenkins, que iniciam scripts de shell com as opções shebang #!/bin/sh -ex.

De acordo comBash Shebang para manequins?, -x, "faz com que o shell imprima um rastreamento de execução", o que é ótimo para a maioria dos propósitos - exceto para ecos:

echo "Message"

produz a saída

+ echo "Message"
Message

o que é um pouco redundante e parece um pouco estranho. Tem como deixar -xhabilitado, mas só saída

Message

em vez das duas linhas acima, por exemplo, prefixando o comando echo com um caractere de comando especial ou redirecionando a saída?

Responder1

Quando você está com crocodilos até o pescoço, é fácil esquecer que o objetivo era drenar o pântano.                    - ditado popular

A questão é sobreecho, e ainda assim a maioria das respostas até agora se concentrou em como inserir um set +xcomando. Há uma solução muito mais simples e direta:

{ echo "Message"; } 2> /dev/null

(Reconheço que talvez não tivesse pensado nisso { …; } 2> /dev/null se não tivesse visto nas respostas anteriores.)

Isso é um pouco complicado, mas, se você tiver um bloco de echocomandos consecutivos, não precisará fazer isso em cada um individualmente:

{
  echo "The quick brown fox"
  echo "jumps over the lazy dog."
} 2> /dev/null

Observe que você não precisa de ponto-e-vírgula quando possui novas linhas.

Você pode reduzir a carga de digitação usandoideia de Kenorb de abrir /dev/nullpermanentemente em um descritor de arquivo não padrão (por exemplo, 3) e depois dizer 2>&3em vez de 2> /dev/nullo tempo todo.


As primeiras quatro respostas no momento da redação deste artigo exigem fazer algo especial (e, na maioria dos casos, complicado) todovez que você faz um echo. Se você realmente quertodos echocomandos para suprimir o rastreamento de execução (e por que não?), você pode fazer isso globalmente, sem usar muito código. Primeiro, notei que os aliases não são rastreados:

$ myfunc()
> {
>     date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016  0:00:00 AM           # Happy Halloween!
$ myfunc
+ myfunc                                # Note that function call is traced.
+ date
Mon, Oct 31, 2016  0:00:01 AM
$ myalias
+ date                                  # Note that it doesn’t say  + myalias
Mon, Oct 31, 2016  0:00:02 AM

(Observe que os seguintes trechos de script funcionam se o shebang for #!/bin/sh, mesmo que /bin/shseja um link para o bash. Mas, se o shebang for #!/bin/bash, você precisará adicionar um shopt -s expand_aliasescomando para que os aliases funcionem em um script.)

Então, para meu primeiro truque:

alias echo='{ set +x; } 2> /dev/null; builtin echo'

Agora, quando dizemos echo "Message", estamos chamando o alias, que não é rastreado. O alias desativa a opção de rastreamento, enquanto suprime a mensagem de rastreamento do setcomando (usando a técnica apresentada primeiro emresposta do usuário5071535) e, em seguida, executa o echocomando real. Isso nos permite obter um efeito semelhante ao da resposta do usuário5071535 sem a necessidade de editar o código emtodo echocomando. No entanto, isso deixa o modo de rastreamento desativado. Não podemos colocar a set -xno alias (ou pelo menos não facilmente) porque um alias só permite que uma string seja substituída por uma palavra; nenhuma parte da string do alias pode ser injetada no comando depoisos argumentos (por exemplo, "Message"). Então, por exemplo, se o script contiver

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date

a saída seria

+ date
Mon, Oct 31, 2016  0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016  0:00:04 AM           # Note that it doesn’t say  + date

então você ainda precisa reativar a opção de rastreamento depois de exibir a(s) mensagem(ns) — mas apenas uma vez após cadabloquearechode comandos consecutivos :

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date


Seria bomse pudéssemos fazer o set -xautomático depois de um echo... e podemos, com um pouco mais de truque. Mas antes de apresentar isso, considere isso. O OP está começando com scripts que usam #!/bin/sh -exshebang. Implicitamente, o usuário poderia remover o xdo shebang e ter um script que funcionasse normalmente, sem rastreamento de execução. Seria bom se pudéssemos desenvolver uma solução que mantivesse essa propriedade. As primeiras respostas aqui falham nessa propriedade porque ativam o rastreamento “de volta” após echoas declarações, incondicionalmente, independentemente de já estar ativado.  Esta respostaclaramente falha em reconhecer essa questão, poissubstitui echosaída com saída de rastreamento; portanto, todas as mensagens desaparecem se o rastreamento estiver desativado. Apresentarei agora uma solução que ativa o rastreamento após uma echodeclaraçãocondicionalmente- somente se já estiver ligado. Rebaixar isso para uma solução que ative o rastreamento “de volta” incondicionalmente é trivial e é deixado como um exercício.

alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
        builtin echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}

$-é a lista de opções; uma concatenação das letras correspondentes a todas as opções definidas. Por exemplo, se as opções ee xestiverem definidas, $-haverá uma confusão de letras que inclui ee x. Meu novo alias (acima) salva o valor $-antes de desligar o rastreamento. Então, com o rastreamento desativado, ele transfere o controle para uma função shell. Essa função faz o real echo e depois verifica se a xopção estava ativada quando o alias foi invocado. Se a opção estava ativada, a função a ativa novamente; se estiver desligado, a função o deixa desligado.

Você pode inserir as sete linhas acima (oito, se incluir um shopt) no início do script e deixar o resto de lado.

Isso permitiria que você

  1. para usar qualquer uma das seguintes linhas shebang:
    #!/bin/sh -ex
    #!/bin/sh -e
    #!/bin/sh –x
    ou simplesmente
    #!/bin/sh
    e deve funcionar conforme o esperado.
  2. ter código como
    (Shebang)
    comando 1
    comando 2
    comando 3
    definir -x
    comando 4
    comando 5
    comando 6
    definir +x
    comando 7
    comando 8
    comando 9
    e
    • Os comandos 4, 5 e 6 serão rastreados — a menos que um deles seja um echo, caso em que será executado, mas não rastreado. (Mas mesmo que o comando 5 seja um echo, o comando 6 ainda será rastreado.)
    • Os comandos 7, 8 e 9 não serão rastreados. Mesmo que o comando 8 seja um echo, o comando 9 ainda não será rastreado.
    • Os comandos 1, 2 e 3 serão rastreados (como 4, 5 e 6) ou não (como 7, 8 e 9) dependendo se o shebang inclui x.

PS: descobri que, no meu sistema, posso omitir a builtinpalavra-chave na minha resposta do meio (aquela que é apenas um apelido para echo). Isto não é surpreendente; bash(1) diz que, durante a expansão do alias,…

… uma palavra idêntica a um alias sendo expandido não é expandida uma segunda vez. Isto significa que se pode aliaslsparals -F, por exemplo, e o bash não tenta expandir recursivamente o texto de substituição.

Não é de surpreender que a última resposta (aquela com echo_and_restore) falhe se a builtinpalavra-chave for omitida 1 . Mas, estranhamente, funciona se eu excluir builtine mudar a ordem:

echo_and_restore() {
        echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'

__________
1  Parece dar origem a um comportamento indefinido. eu tenho visto

  • um loop infinito (provavelmente por causa da recursão ilimitada),
  • uma /dev/null: Bad addressmensagem de erro e
  • um dump principal.

Responder2

Encontrei uma solução parcial emInformeIT:

#!/bin/bash -ex
set +x; 
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"

saídas

set +x; 
shell tracing is disabled here 
+ echo "but is enabled here"
but is enabled here

Infelizmente, isso ainda ecoa set +x, mas pelo menos fica quieto depois disso. então é pelo menos uma solução parcial para o problema.

Mas existe talvez uma maneira melhor de fazer isso? :)

Responder3

Dessa forma, melhora sua própria solução, eliminando a set +xsaída:

#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"

Responder4

Coloque set +xentre colchetes, para que se aplique apenas ao escopo local.

Por exemplo:

#!/bin/bash -x
exec 3<> /dev/null
(echo foo1 $(set +x)) 2>&3
($(set +x) echo foo2) 2>&3
( set +x; echo foo3 ) 2>&3
true

produziria:

$ ./foo.sh 
+ exec
foo1
foo2
foo3
+ true

informação relacionada