Suprimir o rastreamento de execução do bash (set -x) da parte externa do script

17

Tentei encontrar uma resposta para esta pergunta, mas não tive sorte até agora:

Eu tenho um script que executa alguns outros scripts, e muitos desses outros scripts têm "set -x", o que os faz imprimir todos os comandos que executam. Gostaria de me livrar disso, mas reter as informações se algum dos scripts enviar a mensagem de erro ao stderr.

Então não posso simplesmente escrever ./script 2>/dev/null

Além disso, não tenho privilégios para editar esses outros scripts, portanto não posso alterar manualmente a opção de configuração.

Eu estava pensando em registrar tudo, desde stderr ao arquivo separado, e filtrar os comandos de rastreamento, mas talvez haja uma maneira mais simples?

humano
fonte
1
./script 2>some_file
Satō Katsura

Respostas:

25

Com bash4.1 e acima, você pode fazer

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(também funciona quando bashé chamado como sh).

Basicamente, estamos dizendo bashpara produzir a xtracesaída no descritor de arquivo 7 em vez do padrão 2 e redirecionar esse descritor de arquivo para /dev/null. O número fd é arbitrário. Use um fd acima de 2 que não seja usado em seu script. Se o shell no qual você está inserindo esse comando for bashou yash, você pode até usar um número acima de 9 (embora possa haver problemas se o descritor de arquivo for usado internamente pelo shell).

Se o shell do qual você está chamando esse bashscript for zsh, você também pode:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

para que a variável seja atribuída automaticamente ao primeiro fd livre acima de 9.

Para versões mais antigas de bash, outra opção, se a opção xtraceestiver ativada com set -x(em oposição a #! /bin/bash -xou set -o xtrace), seria redefinir setcomo uma função exportada que não faz nada quando aprovada -x(embora isso interrompa o script se ele (ou qualquer outro bashscript que ele chama) usado setpara definir os parâmetros posicionais).

Gostar:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

Outra opção é adicionar uma interceptação DEBUG em um $BASH_ENVarquivo set +xantes de cada comando.

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

Isso não funcionará quando set -xfor feito em um sub-shell.

Como o @ilkkachu disse, desde que você tenha permissão de gravação para qualquer pasta no sistema de arquivos, você deve pelo menos conseguir fazer uma cópia do script e editá-lo.

Se não houver lugar em que você possa escrever uma cópia do script, ou se não for conveniente fazer e editar uma nova cópia sempre que houver uma atualização no script original, você ainda poderá:

 bash <(sed 's/set -x/set +x/g' ./script.bash)

Isso (e a abordagem de cópia) pode não funcionar corretamente se o script faz algo sofisticado $0ou variáveis ​​especiais como $BASH_SOURCE(como procurar arquivos que sejam relativos ao local do próprio script), portanto, talvez seja necessário editar mais, como substitua $0pelo caminho do script ...

Stéphane Chazelas
fonte
Sua primeira resposta é exatamente o que eu precisava, limpa e elegante. Você poderia explicar um pouco como isso funciona? Por que o número 7? Para que outros números poderiam ser usados? Obrigado.
humana
@human, see edit
Stéphane Chazelas
1
O {BASH_XTRACEFD}>truque também funciona em bash4.1 ou posterior.
chepner
@chepner, sim, o recurso foi adicionado ao zsh, ksh93 e bash ao mesmo tempo, por sugestão de um desenvolvedor do zsh. Mas, aqui não funciona para ksh93ou bashem que a variável não é passado no ambiente do comando (compare <shell> -c 'export fd; printenv fd {fd}> /dev/null'em zsh, bashe ksh93). Você poderia fazê-lo funcionar em ksh93/ bashfazendo isso em duas etapas ou possivelmente usando eval, mas bash, para isso, teria efeitos colaterais se a opção xtrace estivesse ativada.
Stéphane Chazelas 17/03/2017
5

Como são scripts, você pode fazer cópias deles e editá-los.

Fora isso, filtrar a saída parece simples, você pode definir explicitamente PS4algo mais incomum que uma única vantagem, para facilitar a filtragem:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(é claro que isso entrará em colapso stdout e stdin, mas a tubulação apenas stderr no Bash fica meio peluda, então eu vou ignorar isso)

ilkkachu
fonte
1
Tubulação apenas stderr é fácil: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Patrick
4
@ Patrick, fazer isso em bashproblemas como grepé executado de forma assíncrona (o bash não está esperando por isso, para que ele possa (e geralmente o faça) emitir coisas após o início do próximo comando no script).
Stéphane Chazelas 17/03/17