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.Encontrei uma solução parcial no InformIT :
saídas
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? :)
fonte
Dessa maneira, você melhora sua própria solução, eliminando a
set +x
saída:fonte
Coloque
set +x
dentro dos colchetes, para que se aplicasse apenas ao escopo local.Por exemplo:
produziria:
fonte
set +x
interior do subshell (ou o uso de subshells) esteja fazendo algo útil. Você pode removê-lo e obter o mesmo resultado. É o stderr de redirecionamento para / dev / null que está fazendo o trabalho de "desabilitar" o rastreio temporariamente ... Parece queecho foo1 2>/dev/null
etc. seria igualmente eficaz e mais legível.bash -x
) e já está redirecionando&2
para nulo (desde que&3
foi redirecionado para nulo), por isso não tenho certeza de como esse comentário é relevante. Talvez apenas precisemos de um exemplo melhor que ilustre seu argumento, mas pelo menos no exemplo dado ainda parece que poderia ser simplificado sem perder nenhum benefício.Adoro a resposta abrangente e bem explicada do g-man e considero a melhor fornecida até agora. Ele se importa com o contexto do script e não força as configurações quando elas não são necessárias. Portanto, se você estiver lendo esta resposta, vá em frente e verifique essa, todo o mérito está aí.
No entanto, nessa resposta, falta uma parte importante: o método proposto não funcionará para um caso de uso típico, ou seja, relatar erros:
Devido à forma como o alias é construído, isso será expandido para
e você adivinhou,
echo_and_restore
é executado sempre , incondicionalmente. Como aset +x
peça não foi executada, significa que o conteúdo dessa função também será impresso.Alterar o último
;
para&&
também não funcionaria, porque no Bash||
e&&
é associativo à esquerda .Encontrei uma modificação que funciona para este caso de uso:
Ele usa um subshell (a
(...)
parte) para agrupar todos os comandos e passa a string de entrada através de stdin como uma String Here (a<<<
coisa) que é impressa porcat -
. O-
é opcional, mas você sabe, " explícito é melhor que implícito ".Você também pode usar
cat -
diretamente sem o eco , mas eu gosto dessa maneira, pois permite adicionar outras strings à saída. Por exemplo, costumo usá-lo assim:E agora funciona lindamente:
fonte
O rastreamento de execução vai para
stderr
, filtre-o desta maneira:Alguma explicação, passo a passo:
stderr
é redirecionado ... -2>
>(…)
grep
é o comando^
+ echo
…grep
inverte a partida ... --v
stdout
; nós o redirecionamos parastderr
onde ele pertence. ->&2
O problema é (eu acho) que esta solução pode dessincronizar os fluxos. Por causa da filtragem,
stderr
pode ser um pouco tarde em relação astdout
(onde aecho
saída pertence por padrão). Para corrigi-lo, você pode ingressar nos fluxos primeiro, se não se importar de tê-los emstdout
:Você pode criar essa filtragem no próprio script, mas essa abordagem é propensa à dessincronização, com certeza (ou seja, ocorreu nos meus testes: o rastreamento da execução de um comando pode aparecer após a saída do seguinte
echo
).O código fica assim:
Execute-o sem truques:
Novamente, use
> >(grep -v "^+ echo ") 2>&1
para manter a sincronização ao custo de ingressar nos fluxos.Outra abordagem. Você obtém uma saída "um pouco redundante" e de aparência estranha porque o seu terminal mistura
stdout
estderr
. Esses dois fluxos são animais diferentes por um motivo. Verifique se a análise atendestderr
apenas às suas necessidades; descartarstdout
:Se você tiver em seu script uma
echo
mensagem de depuração / erro de impressãostderr
, poderá se livrar da redundância da maneira descrita acima. Comando completo:Desta vez, estamos trabalhando
stderr
apenas, portanto, a dessincronização não é mais uma preocupação. Infelizmente, dessa forma, você não verá um rastreio nem uma saídaecho
impressa emstdout
(se houver). Poderíamos tentar reconstruir nosso filtro para detectar redirecionamento (>&2
), mas se você olhar paraecho foobar >&2
,echo >&2 foobar
eecho "foobar >&2"
então você provavelmente vai concordar que as coisas ficam complicadas.Depende muito dos ecos que você possui em seus scripts. Pense duas vezes antes de implementar algum filtro complexo, pois pode sair pela culatra. É melhor ter um pouco de redundância do que acidentalmente perder algumas informações cruciais.
Em vez de descartar o rastreio de execução de um
echo
, podemos descartar sua saída - e qualquer saída, exceto os rastreios. Para analisar apenas os rastreamentos de execução, tente:Infalível? Não. Pense no que acontecerá se houver
echo "+ rm -rf --no-preserve-root /" >&2
no script. Alguém pode sofrer um ataque cardíaco.E finalmente…
Felizmente, há
BASH_XTRACEFD
variável ambiental. Deman bash
:Podemos usá-lo assim:
Observe que a primeira linha gera um subshell. Dessa forma, o descritor de arquivo não permanecerá válido nem a variável atribuída no shell atual posteriormente.
Graças a
BASH_XTRACEFD
você, você pode analisar traços livres de ecos e quaisquer outras saídas, quaisquer que sejam. Não é exatamente o que você queria, mas minha análise me faz pensar que este é (em geral) o caminho certo.É claro que você pode usar outro método, especialmente quando precisar analisar
stdout
e / oustderr
acompanhar seus rastreamentos. Você só precisa se lembrar de que existem certas limitações e armadilhas. Eu tentei mostrar (alguns) deles.fonte
Em um Makefile você pode usar o
@
símbolo.Exemplo de uso:
@echo 'message'
A partir deste documento GNU:
fonte
./test.sh: line 1: @echo: command not found
, mas estou usando o bash.@
só funciona quando você está ecoando em makefiles.Editado em 29 de outubro de 2016 por moderador, sugere que o original não continha informações suficientes para entender o que está acontecendo.
Este "truque" produz apenas uma linha de saída de mensagem no terminal quando o xtrace está ativo:
A pergunta original é : existe uma maneira de deixar -x ativado, mas apenas gera uma mensagem em vez das duas linhas . Esta é uma citação exata da pergunta.
Entendo a pergunta: como "deixar o set -x ativado E produzir uma única linha para uma mensagem"?
Portanto, em resumo, o OP exige:
O OP não exige que o comando echo seja usado. Eles o citaram como um exemplo de produção de mensagens, usando a abreviação, por exemplo, que significa Latin examplei gratia, ou "por exemplo".
Pensando "fora da caixa" e abandonando o uso de eco para produzir mensagens, observo que uma declaração de atribuição pode atender a todos os requisitos.
Faça uma atribuição de uma sequência de texto (contendo a mensagem) a uma variável inativa.
A adição desta linha a um script não altera nenhuma lógica, mas produz uma única linha quando o rastreamento está ativo: (Se você estiver usando a variável $ echo , altere o nome para outra variável não utilizada)
O que você verá no terminal é de apenas 1 linha:
não dois, como o OP não gosta:
Aqui está um exemplo de script para demonstrar. Observe que a substituição de variável em uma mensagem (o nome do diretório $ HOME 4 linhas a partir do final) funciona, para que você possa rastrear variáveis usando esse método.
E aqui está a saída de executá-lo na raiz, depois no diretório inicial.
fonte
echo "Here are the files in this directory:" *
?