Suprime o rastreio de execução bash (set -x) do lado de fora do script

17

Eu tentei encontrar uma resposta para essa pergunta, mas até agora não tive sorte:

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

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

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

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

    
por human 17.03.2017 / 13:01

2 respostas

24

Com bash 4.1 e acima, você pode fazer

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

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

Basicamente, estamos dizendo bash para gerar a saída xtrace 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 no seu script. Se o shell em que você está inserindo este comando for bash ou yash , você pode até usar um número acima de 9 (embora você possa ter problemas se o descritor de arquivo for usado internamente pelo shell).

Se o shell do qual você está chamando o script bash for zsh , você também poderá fazer:

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

para a variável a ser atribuída automaticamente o primeiro fd livre acima de 9.

Para versões anteriores de bash , outra opção, se o xtrace estiver ativado com set -x (em oposição a #! /bin/bash -x ou set -o xtrace ) seria redefinir set como uma função exportada não faz nada quando passa -x (embora isso quebre o script se ele (ou qualquer outro script bash invocado) usar set para definir os parâmetros posicionais).

Como:

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 arquivo $BASH_ENV que executa set +x antes de cada comando.

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

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

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

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

 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 fizer algo interessante com $0 ou variáveis especiais como $BASH_SOURCE (como procurar por arquivos relativos à localização do próprio script), portanto talvez você precise fazer mais algumas edições, como substituir $0 pelo caminho do script ...

    
por 17.03.2017 / 13:47
5

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

Além disso, filtrar a saída pareceria simples, você poderia definir explicitamente PS4 para algo mais incomum do que uma única adição, para facilitar a filtragem:

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

(é claro que isso colapsa stdout e stdin, mas piping apenas stderr em Bash fica um pouco peludo, então eu vou ignorar isso)

    
por 17.03.2017 / 13:11

Tags