Lorsque vous êtes à la hauteur de votre cou en alligators, il est facile d'oublier que le but était de drainer le marais.
- dicton populaire
La question porte sur echo
, et pourtant la majorité des réponses jusqu'à présent se sont concentrées sur la façon d'introduire une set +x
commande. Il existe une solution beaucoup plus simple et plus directe:
{ echo "Message"; } 2> /dev/null
(Je reconnais que je n'aurais peut-être pas pensé au { …; } 2> /dev/null
si je ne l'avais pas vu dans les réponses précédentes.)
C'est un peu lourd, mais si vous avez un bloc de echo
commandes consécutives , vous n'avez pas besoin de le faire sur chacune d'elles individuellement:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
Notez que vous n'avez pas besoin de points-virgules lorsque vous avez des retours à la ligne.
Vous pouvez réduire la charge de frappe en utilisant l'idée
de kenorb d'ouvrir en /dev/null
permanence sur un descripteur de fichier non standard (par exemple, 3), puis en disant 2>&3
au lieu de 2> /dev/null
tout le temps.
Les quatre premières réponses au moment d'écrire ces lignes nécessitent de faire quelque chose de spécial (et, dans la plupart des cas, lourd) à
chaque fois que vous faites un echo
. Si vous voulez vraiment que toutes les echo
commandes suppriment la trace d'exécution (et pourquoi pas?), Vous pouvez le faire globalement, sans munging beaucoup de code. Tout d'abord, j'ai remarqué que les alias ne sont pas tracés:
$ 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
(Notez que les extraits de script suivants fonctionnent si le shebang est #!/bin/sh
, même s'il /bin/sh
s'agit d'un lien vers bash. Mais, si le shebang l'est #!/bin/bash
, vous devez ajouter une shopt -s expand_aliases
commande pour que les alias fonctionnent dans un script.)
Donc, pour mon premier tour:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
Maintenant, quand nous disons echo "Message"
, nous appelons l'alias, qui n'est pas tracé. L'alias désactive l'option de trace, tout en supprimant le message de trace de la set
commande (en utilisant la technique présentée en premier dans la réponse de user5071535 ), puis exécute la echo
commande réelle . Cela nous permet d'obtenir un effet similaire à celui de la réponse de user5071535 sans avoir à modifier le code à chaque echo
commande. Cependant, cela laisse le mode trace désactivé. Nous ne pouvons pas mettre un set -x
dans l'alias (ou du moins pas facilement) car un alias permet uniquement de substituer une chaîne à un mot; aucune partie de la chaîne d'alias ne peut être injectée dans la commande
après les arguments (par exemple, "Message"
). Ainsi, par exemple, si le script contient
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
la sortie serait
+ 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
vous devez donc réactiver l'option trace après avoir affiché le (s) message (s) - mais seulement une fois après chaque bloc de echo
commandes consécutives :
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Ce serait bien si nous pouvions faire l' set -x
automatique après un echo
- et nous pouvons, avec un peu plus de ruse. Mais avant de présenter cela, réfléchissez à cela. L'OP commence avec des scripts qui utilisent un #!/bin/sh -ex
shebang. Implicitement, l'utilisateur pourrait supprimer le x
du shebang et avoir un script qui fonctionne normalement, sans traçage d'exécution. Ce serait bien si nous pouvions développer une solution qui conserve cette propriété. Les premières réponses ici échouent à cette propriété car elles réactivent le traçage des echo
déclarations après , sans condition, sans se soucier de savoir si c'était déjà le cas.
Cette réponse ne reconnaît visiblement pas ce problème, car elle remplace echo
sortie avec sortie trace; par conséquent, tous les messages disparaissent si le suivi est désactivé. Je vais maintenant présenter une solution qui réactivera le traçage après une echo
déclaration conditionnellement - uniquement si elle était déjà activée. La rétrogradation à une solution qui active le «retour» du traçage sans condition est triviale et est laissée comme exercice.
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
}
$-
est la liste des options; une concaténation des lettres correspondant à toutes les options définies. Par exemple, si les options e
et x
sont définies, alors $-
sera un mélange de lettres qui comprend e
et x
. Mon nouvel alias (ci-dessus) enregistre la valeur de $-
avant de désactiver le traçage. Ensuite, avec le suivi désactivé, il jette le contrôle sur une fonction shell. Cette fonction fait le réel echo
et vérifie ensuite si l' x
option a été activée lorsque l'alias a été appelé. Si l'option était activée, la fonction la rallume; s'il était désactivé, la fonction le laisse désactivé.
Vous pouvez insérer les sept lignes ci-dessus (huit, si vous incluez un shopt
) au début du script et laisser le reste tranquille.
Cela vous permettrait
- d'utiliser l'une des lignes de shebang suivantes:
#! / bin / sh -ex
#! / bin / sh -e
#! / bin / sh –x
ou tout simplement#! / bin / sh
et cela devrait fonctionner comme prévu.
- avoir du code comme
(shebang)
commande 1
commande 2
commande 3
set -x
commande 4
commande 5
commande 6
set + x
commande 7
commande 8
commande 9
et
- Les commandes 4, 5 et 6 seront tracées - sauf si l'une d'elles est un
echo
, auquel cas elle sera exécutée mais pas tracée. (Mais même si la commande 5 est un echo
, la commande 6 sera toujours tracée.)
- Les commandes 7, 8 et 9 ne seront pas tracées. Même si la commande 8 est un
echo
, la commande 9 ne sera toujours pas tracée.
- Les commandes 1, 2 et 3 seront tracées (comme 4, 5 et 6) ou non (comme 7, 8 et 9) selon que le shebang inclut
x
.
PS J'ai découvert que, sur mon système, je peux laisser de côté le builtin
mot - clé dans ma réponse du milieu (celui qui est juste un alias echo
). Ce n'est pas surprenant; bash (1) dit que, pendant l'expansion des alias,…
… Un mot identique à un alias en cours de développement n'est pas développé une deuxième fois. Cela signifie que l' on peut alias ls
pour ls -F
, par exemple, et bash ne cherche pas à développer récursive le texte de remplacement.
Sans surprise, la dernière réponse (celle avec echo_and_restore
) échoue si le builtin
mot-clé est omis 1 . Mais, curieusement, cela fonctionne si je supprime le builtin
et change la commande:
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 Il semble donner lieu à un comportement indéfini. j'ai vu
- une boucle infinie (probablement à cause d'une récursion illimitée),
- un
/dev/null: Bad address
message d'erreur, et
- un core dump.
echo +x; echo "$*"; echo -x
un alias, j'aimerais le voir.J'ai trouvé une solution partielle chez InformIT :
les sorties
Malheureusement, cela fait toujours écho
set +x
, mais au moins c'est calme après ça. c'est donc au moins une solution partielle au problème.Mais existe-t-il peut-être une meilleure façon de procéder? :)
la source
De cette façon, vous améliorez votre propre solution en vous débarrassant de la
set +x
sortie:la source
Mettez
set +x
entre crochets, donc cela ne s'appliquerait qu'à la portée locale.Par exemple:
produirait:
la source
set +x
intérieur du sous-shell (ou en utilisant des sous-coquilles du tout) fasse quelque chose d'utile. Vous pouvez le supprimer et obtenir le même résultat. C'est la redirection de stderr vers / dev / null qui fait le travail de "désactivation" temporaire du traçage ... Il sembleecho foo1 2>/dev/null
, etc., serait tout aussi efficace et plus lisible.bash -x
) et vous redirigez déjà vers&2
null (puisqu'il a&3
été redirigé vers null), donc je ne sais pas en quoi ce commentaire est pertinent. Peut-être que nous avons juste besoin d'un meilleur exemple qui illustre votre point, mais dans l'exemple donné au moins, il semble toujours qu'il pourrait être simplifié sans perdre aucun avantage.J'adore la réponse complète et bien expliquée de g-man , et je la considère comme la meilleure fournie jusqu'à présent. Il se soucie du contexte du script et ne force pas les configurations lorsqu'elles ne sont pas nécessaires. Donc, si vous lisez d'abord cette réponse, allez-y et vérifiez celle-ci, tout le mérite est là.
Cependant, dans cette réponse, il manque un élément important: la méthode proposée ne fonctionnera pas pour un cas d'utilisation typique, c'est-à-dire signaler des erreurs:
En raison de la façon dont l'alias est construit, cela s'étendra à
et vous l'avez deviné,
echo_and_restore
s'exécute toujours , sans condition. Étant donné que laset +x
pièce ne s'est pas exécutée, cela signifie que le contenu de cette fonction sera également imprimé.Modification de la dernière
;
à&&
ne fonctionnerait pas non plus , parce que dans Bash,||
et&&
sont associatifs gauche .J'ai trouvé une modification qui fonctionne pour ce cas d'utilisation:
Il utilise un sous-shell (la
(...)
partie) afin de regrouper toutes les commandes, puis passe la chaîne d'entrée via stdin en tant que chaîne Here (la<<<
chose) qui est ensuite imprimée parcat -
. Le-
est facultatif, mais vous savez, " explicite vaut mieux qu'implicite ".Vous pouvez également utiliser
cat -
directement sans l' écho , mais j'aime cette façon car cela me permet d'ajouter d'autres chaînes à la sortie. Par exemple, j'ai tendance à l'utiliser comme ceci:Et maintenant, cela fonctionne à merveille:
la source
La trace d'exécution va à
stderr
, filtrez-la de cette façon:Quelques explications, étape par étape:
stderr
est redirigé ... -2>
>(…)
grep
est la commande…^
+ echo
…grep
inverse le match ... --v
stdout
; nous le redirigeons làstderr
où il appartient. ->&2
Le problème est (je suppose) que cette solution peut désynchroniser les flux. En raison du filtrage
stderr
peut être un peu en retard par rapport àstdout
(où laecho
sortie appartient par défaut). Pour y remédier, vous pouvez d'abord rejoindre les flux si cela ne vous dérange pas de les avoir tous les deux dansstdout
:Vous pouvez créer un tel filtrage dans le script lui-même, mais cette approche est certainement sujette à la désynchronisation (c'est-à-dire qu'elle s'est produite dans mes tests: la trace d'exécution d'une commande peut apparaître après la sortie de la suite
echo
).Le code ressemble à ceci:
Exécutez-le sans aucune astuce:
Encore une fois, utilisez
> >(grep -v "^+ echo ") 2>&1
pour maintenir la synchronisation au prix de la jonction des flux.Une autre approche. Vous obtenez une sortie "un peu redondante" et d'aspect étrange car votre terminal mélange
stdout
etstderr
. Ces deux courants sont des animaux différents pour une raison. Vérifiez si l'analysestderr
ne correspond qu'à vos besoins; jeterstdout
:Si vous avez dans votre script un
echo
message d'erreur de débogage / d'erreur,stderr
vous pouvez vous débarrasser de la redondance de la manière décrite ci-dessus. Commande complète:Cette fois, nous travaillons
stderr
uniquement, la désynchronisation n'est donc plus un problème. Malheureusement, de cette façon, vous ne verrez ni trace ni sortie deecho
cette copie versstdout
(le cas échéant). Nous pourrions essayer de reconstruire notre filtre pour détecter la redirection (>&2
) mais si vous regardezecho foobar >&2
,echo >&2 foobar
etecho "foobar >&2"
alors vous serez probablement d'accord pour dire que les choses se compliquent.Cela dépend beaucoup des échos que vous avez dans vos scripts. Réfléchissez bien avant d'implémenter un filtre complexe, cela pourrait se retourner contre vous. Il vaut mieux avoir un peu de redondance que de manquer accidentellement des informations cruciales.
Au lieu de supprimer la trace d'exécution d'un,
echo
nous pouvons supprimer sa sortie - et toute sortie à l'exception des traces. Pour analyser uniquement les traces d'exécution, essayez:Infaillible? Pensez à ce qui se passera s'il y en a
echo "+ rm -rf --no-preserve-root /" >&2
dans le script. Quelqu'un pourrait avoir une crise cardiaque.Et enfin…
Heureusement, il existe
BASH_XTRACEFD
une variable environnementale. Deman bash
:Nous pouvons l'utiliser comme ceci:
Notez que la première ligne génère un sous-shell. De cette façon, le descripteur de fichier ne restera pas valide ni la variable affectée par la suite dans le shell courant.
Grâce à
BASH_XTRACEFD
vous, vous pouvez analyser les traces sans échos et autres sorties, quelles qu'elles soient. Ce n'est pas exactement ce que vous vouliez mais mon analyse me fait penser que c'est (en général) la bonne façon.Bien sûr, vous pouvez utiliser une autre méthode, surtout lorsque vous devez analyser
stdout
et / oustderr
accompagner vos traces. Vous avez juste besoin de vous rappeler qu'il y a certaines limites et pièges. J'ai essayé de les montrer (certains).la source
Dans un Makefile, vous pouvez utiliser le
@
symbole.Exemple d'utilisation:
@echo 'message'
De ce document GNU:
la source
./test.sh: line 1: @echo: command not found
, mais j'utilise bash.@
ne fonctionne que lorsque vous faites écho dans les makefiles.Modifié le 29 octobre 2016 par le modérateur, suggère que l'original ne contenait pas suffisamment d'informations pour comprendre ce qui se passe.
Cette "astuce" produit une seule ligne de sortie de message sur le terminal lorsque xtrace est actif:
La question d'origine est Y a - t-il un moyen de laisser -x activé, mais uniquement de générer un message au lieu des deux lignes . Ceci est une citation exacte de la question.
Je comprends que la question soit, comment "laisser set -x activé ET produire une seule ligne pour un message"?
Donc en résumé, le PO nécessite:
L'OP n'exige pas que la commande echo soit utilisée. Ils l'ont cité comme un exemple de production de messages, en utilisant l'abréviation par exemple qui signifie latin exempli gratia, ou "par exemple".
En pensant "hors des sentiers battus" et en abandonnant l'utilisation de l' écho pour produire des messages, je note qu'une déclaration d'affectation peut répondre à toutes les exigences.
Effectuez une affectation d'une chaîne de texte (contenant le message) à une variable inactive.
L'ajout de cette ligne à un script ne modifie aucune logique, mais produit une seule ligne lorsque le traçage est actif: (Si vous utilisez la variable $ echo , changez simplement le nom en une autre variable inutilisée)
Ce que vous verrez sur le terminal est une seule ligne:
pas deux, car l'OP n'aime pas:
Voici un exemple de script à démontrer. Notez que la substitution de variables dans un message (le nom du répertoire $ HOME à 4 lignes de la fin) fonctionne, vous pouvez donc tracer des variables à l'aide de cette méthode.
Et voici la sortie de l'exécuter à la racine, puis le répertoire personnel.
la source
echo "Here are the files in this directory:" *
?