Supprimer la trace d'exécution bash (set -x) de l'extérieur du script

17

J'ai essayé de trouver une réponse à cette question, mais je n'ai pas eu de chance jusqu'à présent:

J'ai un script qui exécute d'autres scripts, et bon nombre de ces autres scripts contiennent "set -x", ce qui les oblige à imprimer chaque commande qu'ils exécutent. Je voudrais m'en débarrasser mais conserver les informations si l'un des scripts envoie le message d'erreur à stderr.

Je ne peux donc pas simplement écrire ./script 2>/dev/null

De plus, je n'ai pas les privilèges pour modifier ces autres scripts, donc je ne peux pas modifier manuellement l'option définie.

Je pensais à tout enregistrer de stderr au fichier séparé et filtrer les commandes de traçage, mais peut-être y a-t-il un moyen plus simple?

Humain
la source
1
./script 2>some_file
Satō Katsura

Réponses:

25

Avec bash4.1 et supérieur, vous pouvez faire

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

(fonctionne également lorsque bashest invoqué en tant que sh).

Fondamentalement, nous disons bashde sortir la xtracesortie sur le descripteur de fichier 7 au lieu de la valeur par défaut de 2, et de rediriger ce descripteur de fichier vers /dev/null. Le nombre fd est arbitraire. Utilisez un fd supérieur à 2 qui n'est pas utilisé autrement dans votre script. Si le shell dans lequel vous entrez cette commande est bashou yash, vous pouvez même utiliser un nombre supérieur à 9 (bien que vous puissiez rencontrer des problèmes si le descripteur de fichier est utilisé en interne par le shell).

Si le shell à partir duquel vous appelez ce bashscript est zsh, vous pouvez également faire:

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

pour que la variable soit automatiquement affectée au premier fd libre supérieur à 9.

Pour les anciennes versions de bash, une autre option, si le xtraceest activé avec set -x(par opposition à #! /bin/bash -xou set -o xtrace) serait de redéfinir setcomme une fonction exportée qui ne fait rien une fois passée -x(bien que cela briserait le script s'il (ou tout autre bashscript qu'il invoque) utilisé setpour définir les paramètres de position).

Comme:

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

Une autre option consiste à ajouter une interruption DEBUG dans un $BASH_ENVfichier qui le fait set +xavant chaque commande.

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

Cela ne fonctionnera pas quand set -xc'est fait dans un sous-shell.

Comme l'a dit @ilkkachu, à condition d'avoir la permission d'écrire sur n'importe quel dossier du système de fichiers, vous devriez au moins être en mesure de faire une copie du script et de le modifier.

S'il n'y a nulle part où vous pouvez écrire une copie du script, ou s'il n'est pas pratique de créer et de modifier une nouvelle copie chaque fois qu'il y a une mise à jour du script original, vous pouvez toujours faire:

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

Cela (et l'approche de copie) peut ne pas fonctionner correctement si le script fait quelque chose d'extraordinaire avec $0ou des variables spéciales comme $BASH_SOURCE(comme la recherche de fichiers qui sont relatifs à l'emplacement du script lui-même), vous devrez donc peut-être faire plus d'édition comme remplacer $0par le chemin du script ...

Stéphane Chazelas
la source
Votre première réponse est exactement ce dont j'avais besoin, propre et élégante. Pourriez-vous expliquer un peu comment cela fonctionne? Pourquoi le numéro 7? À quoi pourraient servir d'autres numéros? Merci.
humain
@human, voir edit
Stéphane Chazelas
1
L' {BASH_XTRACEFD}>astuce fonctionne également dans bash4.1 ou version ultérieure.
chepner
@chepner, oui, la fonctionnalité a été ajoutée à zsh, ksh93 et ​​bash en même temps sur suggestion d'un développeur zsh. Mais, ici , il ne fonctionne pas pour ksh93ou bashen ce que la variable ne passe pas dans l'environnement de la commande (comparer <shell> -c 'export fd; printenv fd {fd}> /dev/null'à zsh, bashet ksh93). Vous pouvez le faire fonctionner en ksh93/ bashen le faisant en deux étapes ou éventuellement en utilisant eval, mais pour bash, cela aurait des effets secondaires si l'option xtrace était activée.
Stéphane Chazelas
5

Puisqu'il s'agit de scripts, vous pouvez en faire des copies et les modifier.

En dehors de cela, le filtrage de la sortie semble simple, vous pouvez explicitement définir PS4quelque chose de plus inhabituel qu'un simple plus, pour rendre le filtrage plus facile:

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

(bien sûr, cela réduira stdout et stdin, mais la tuyauterie juste stderr dans Bash devient un peu poilue, donc je l'ignorerai)

ilkkachu
la source
1
La tuyauterie juste stderr est facile: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Patrick
4
@Patrick, faire cela dans basha des problèmes car il grepest exécuté de manière asynchrone (bash ne l'attend pas, donc il peut (et le fait souvent) sortir des choses après le démarrage de la prochaine commande dans le script).
Stéphane Chazelas