Quando você chega ao pescoço em jacarés, é fácil esquecer que o objetivo era drenar o pântano.
- provérbio popular
A pergunta é sobre echo
, e, no entanto, a maioria das respostas até agora se concentrou em como ocultar um set +x
comando. Há uma solução muito mais simples e direta:
{ echo "Message"; } 2> /dev/null
(Reconheço que talvez eu 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 echo
comandos 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 usando a idéia do kenorb
de abrir /dev/null
permanentemente em um descritor de arquivo não padrão (por exemplo, 3) e depois dizendo em 2>&3
vez de 2> /dev/null
o tempo todo.
As quatro primeiras respostas no momento da redação deste documento exigem fazer algo especial (e, na maioria dos casos, complicado)
toda vez que você faz um echo
. Se você realmente deseja que todos os echo
comandos suprimam o rastreamento de execução (e por que não?), Você pode fazê-lo globalmente, sem alterar 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 se /bin/sh
for um link para o bash. Mas, se o shebang for #!/bin/bash
, você precisará adicionar um shopt -s expand_aliases
comando para que os aliases funcionem em um script.)
Então, para o 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 set
comando (usando a técnica apresentada primeiro na resposta do usuário5071535 ) e, em seguida, executa o echo
comando real . Isso nos permite obter um efeito semelhante ao da resposta do usuário5071535 sem precisar editar o código a cada echo
comando. No entanto, isso deixa o modo de rastreamento desativado. Não podemos colocar um set -x
no alias (ou pelo menos não facilmente) porque um alias permite apenas que uma string seja substituída por uma palavra; nenhuma parte da cadeia de alias pode ser injetada no comando
após os 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
portanto, você ainda precisa ativar a opção de rastreamento novamente após exibir as mensagens - mas apenas uma vez após cada bloco de echo
comandos consecutivos :
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Seria bom se pudéssemos fazer o set -x
automá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 um #!/bin/sh -ex
shebang. Implicitamente, o usuário pode remover o x
do shebang e ter um script que funcione normalmente, sem rastreamento de execução. Seria bom se pudéssemos desenvolver uma solução que retenha essa propriedade. As primeiras respostas aqui falham nessa propriedade porque ativam o rastreio após as echo
instruções após , incondicionalmente, sem considerar se ela já estava ativada.
Esta resposta conspicuamente falha em reconhecer esse problema, pois substitui echo
saída com saída de rastreamento; portanto, todas as mensagens desaparecem se o rastreamento estiver desativado. Agora, apresentarei uma solução que ativa o rastreamento após uma echo
declaração condicionalmente - apenas se ela já estiver ativada. Fazer o downgrade para uma solução que ative o retorno 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 e
e x
estiverem definidas, $-
haverá um amontoado de letras que inclui e
e x
. Meu novo alias (acima) salva o valor de $-
antes de desativar o rastreio. Em seguida, com o rastreamento desativado, ele lança o controle sobre uma função shell. Essa função faz o real echo
e depois verifica se a x
opção foi ativada quando o alias foi chamado. Se a opção estava ativada, a função a ativa novamente; se estiver desativado, a função o deixará desativado.
Você pode inserir as sete linhas acima (oito, se você incluir um shopt
) no início do script e deixar o resto em paz.
Isso permitiria que você
- para usar qualquer uma das seguintes linhas shebang:
#! / bin / sh -ex
#! / bin / sh -e
#! / bin / sh –x
ou simplesmente#! / bin / sh
e deve funcionar como esperado.
- ter código como
comando
(shebang) 1 comando 2
comando 3
set -x
comando 4
comando 5
comando 6
set + 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
; nesse caso, ele 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 se o comando 8 for 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 da inclusão do shebang
x
.
PS Descobri que, no meu sistema, posso deixar de fora a builtin
palavra - chave na minha resposta do meio (aquela que é apenas um apelido echo
). Isto não é surpreendente; o 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. Isso significa que é possível usar um alias ls
para ls -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 builtin
palavra-chave for omitida 1 . Mas, estranhamente, funciona se eu excluir o builtin
e alternar 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 de recursão ilimitada),
- uma
/dev/null: Bad address
mensagem de erro e
- um core dump.
echo +x; echo "$*"; echo -x
em um apelido, eu gostaria de vê-lo.