J'essaye d'append une option de debugging à un de mes scripts. Normalement, je veux cacher toute sortie, comme les avertissements, etc, donc je mets >/dev/null 2>&1
derrière beaucoup de commands.
Maintenant, quand je veux déboguer mon script, je devrais les supprimer manuellement, ou mettre toutes les commands à l'intérieur d'un test if ... fi
pour une variable $DEBUG
.
Je pensais mettre >/dev/null 2>&1
intérieur d'une variable ( $REDIR
) et écrire la command arg1 $REDIR
ferait l'affaire. Si je veux déboguer, il me $REDIR
de laisser $REDIR
vide.
Mais un court test sur ma coquille m'a montré que ça ne marcherait pas comme ça:
~$ echo "bla" >/dev/null 2>&1 ~$ REDIR=>/dev/null 2>&1 ~$ echo "bla" $REDIR bla ~$
En utilisant "
ou '
around '
>/dev/null 2>&1
ne fonctionnait pas non plus pour des raisons évidentes.
Alors pourquoi mon idée ne fonctionne pas ici? Ai-je mal compris quelque chose à propos de la mise des commands etc. dans les variables et de les appeler?
Dans ce but, je définis habituellement une fonction comme run
. Cela peut gérer correctement les args avec des espaces et d'autres dans la plupart des cas.
#!/bin/bash run() { if $DEBUG; then v=$(exec 2>&1 && set -x && set -- "$@") echo "#${v#*--}" "$@" else "$@" >/dev/null 2>&1 fi } DEBUG=false run echo "bla" DEBUG=true run echo "bla" run printf "%s . %s . %s\n" bla "more bla" bla
Sortie:
$ bash debug.sh # echo bla bla # printf '%s . %s . %s\n' bla 'more bla' bla bla . more bla . bla
La redirection n'est pas une command, vous ne pouvez donc pas l'exécuter de cette manière. Vous pouvez le faire si vous utilisez eval
, mais cela ouvre une boîte de Pandore.
Une meilleure méthode pour faire ce que vous essayez de faire est d'avoir une fonction pour déboguer la sortie:
function debugprint { if [ ! -z "$debug" ]; then echo "$1" fi } debugprint "$(echo 'bla' 2>&1)"
Cela redirecta l'erreur standard vers la sortie standard, puis debugprint
avec la sortie comme argument. Maintenant, tout ce que vous devez faire est de mettre $debug
sur quelque chose de non vide lorsque vous voulez déboguer.
Bien sûr, cela ouvre aussi une (différente) boîte de vers (liée à la citation). Vous pouvez simplement utiliser set -x
place, ce qui peut ou ne peut pas faire assez pour vos besoins de debugging.
Dans votre cas, echo traite $REDIR
comme argument de string. Vous voulez quelque chose comme:
~$ echo "bla" >/dev/null 2>&1 ~$ REDIR='>/dev/null 2>&1' ~$ eval "echo bla $REDIR" ~$
Cependant, à less que vous essayiez de faire un hack rapide et sale, Wouter Verhelst a la meilleure solution (et ce n'est vraiment pas si long ou compliqué).
peut-être que vous pourriez utiliser une fonction pour diriger la sortie vers et le laisser faire la redirection:
#! /bin/bash DEBUGMODE="Debug" function handleStdOut { if [[ "$DEBUGMODE" == "Debug" ]] then echo "Debugging..." cat echo "Done" else cat > /dev/null fi } echo "bla" | handleStdOut
Pour chaque fonction shell que j'écris, je fais exactement la même chose.
fn(){ echo some normal stderr debug stuff >&2 #if $DBG 2>stderr dd if="\$DBG/please/report/on/this/file" #ditto echo I DEFINITELY need to handle this >&3 #always stderr ( PATH=; ".some" oops I expect to handle ) 2>&4 #always /dev/null echo and the regular stuff #always unaffected } 4<>/dev/null 3>&2 2>&"$((${#DBG}?3:4))"
J'aime ça pour quelques raisons.
L'utilisation de l'évaluation ${#DBG}
pour len garantit toujours une valeur entière > = 0 pour le test, indépendamment de ce que $DBG
pourrait contenir. Cela rend le calcul sûr même si DBG=IFS=0
, par exemple.
A chaque fois que la fonction s'exécute, elle ne doit faire que l' open()
sur /dev/null
la seule fois – à tout autre moment je redirige vers /dev/null
Je le fais à un descripteur établi dans #fd>&4
.
La sortie de debugging – comme je pourrais l'activer avec set -x
– pour les fonctions de mes shells interactifs est vidée par défaut sauf si je fixe explicitement la variable d'environnement $DBG
sur une valeur non nulle.
$DBG
n'est pas nul, l'extension mathématique de la redirection pointera sur elle-même – elle sera évaluée à 2>&3
. 2>&4
et va donc au descripteur open /dev/null
. ~/.sh/fn/...
, mais la command line à laquelle il participe est probablement une autre question tout à fait si j'ai set -x
. $DBG
est null / unset mais set -x
est activé car il ouvre une sortie eval par command dans $PS4
qui ne le fait pas à stderr mais #fd>&3
peut toujours y arriver. Il permet l'inheritance d'une manière saine.
$DBG
d'une manière quelconque qui pourrait leur permettre de commencer à écrire stderr par défaut, si ce n'est déjà possible. $DBG
n'est pas défini ou null toutes les fonctions enfant, il perd même la #fd3
less qu'il ne les appelle avec child_fn 2>&3
– dans ce cas ils auront la même possibilité que leur parent d'écrire explicitement à stderr. $DBG
pour calmer ces fonctions enfant même si $DBG
est déjà défini. $DBG
pour que les fonctions de niveau supérieur appelées après cela activent la sortie stderr par défaut. Je conserve une copy de stderr sur #fd>&3
et donc si la fonction doit pouvoir écrire stderr explicitement sur ce descripteur (sauf comme noté ci-dessus) .
Les descripteurs se ferment et n'affectent pas les valeurs de shell actuelles pour fds 2,3,4 car ils ne sont associés qu'à la command compound qui enveloppe la fonction.
{3,4}>&-
n'est jamais nécessaire.