J'ai utilisé l'un des éléments suivants
echo $(stuff)
echo `stuff`
(où stuff
est par exemple pwd
ou date
ou quelque chose de plus compliqué).
Puis on m'a dit que cette syntaxe était erronée, une mauvaise pratique, non élégante, excessive, redondante, trop compliquée, une programmation culte du fret, noobish, naïf etc.
Mais la commande ne travail, donc ce qui est faux exactement avec elle?
shell-script
sh
echo
Kamil Maciorowski
la source
la source
echo $(echo $(echo $(echo$(echo $(stuff)))))
également fait le travail, et encore vous probablement pas l' utiliser, non? ;-)stuff
. : DRéponses:
tl; dr
Une semelle
stuff
fonctionnerait très probablement pour vous.Réponse complète
Ce qui se produit
Lorsque vous exécutez
foo $(stuff)
, voici ce qui se passe:stuff
court;$(stuff)
dans l'invocation defoo
;foo
s'exécute ensuite , ses arguments de ligne de commande dépendent évidemment de ce quistuff
est retourné.Ce
$(…)
mécanisme est appelé "substitution de commande". Dans votre cas, la commande principale est celleecho
qui affiche essentiellement ses arguments de ligne de commande sur stdout. Donc, tout ce quistuff
essaie d'imprimer sur stdout est capturé, passéecho
et imprimé sur stdout parecho
.Si vous souhaitez que la sortie de
stuff
soit imprimée sur stdout, exécutez simplement la semellestuff
.La
`…`
syntaxe sert le même but que$(…)
(sous le même nom: "substitution de commande"), il y a cependant peu de différences, vous ne pouvez donc pas les échanger aveuglément. Voir cette FAQ et cette question .Dois-je éviter
echo $(stuff)
quoi qu'il arrive?Il y a une raison que vous voudrez peut-être utiliser
echo $(stuff)
si vous savez ce que vous faites. Pour la même raison, vous devriez éviterecho $(stuff)
si vous ne savez pas vraiment ce que vous faites.Le point est
stuff
etecho $(stuff)
n'est pas exactement équivalent. Ce dernier signifie appeler l'opérateur split + glob sur la sortie destuff
avec la valeur par défaut de$IFS
. Le double guillemet de la substitution de commande empêche cela. Le simple guillemet de la substitution de commande fait qu'il ne s'agit plus d'une substitution de commande.Pour observer cela lorsqu'il s'agit de fractionner, exécutez ces commandes:
Et pour le globbing:
Comme vous pouvez le voir
echo "$(stuff)"
est équivalent (-ish *) àstuff
. Vous pouvez l'utiliser, mais à quoi bon compliquer les choses de cette façon?D'un autre côté, si vous souhaitez que la sortie de
stuff
subisse un fractionnement + globalisation, vous pouvez trouverecho $(stuff)
utile. Mais cela doit être votre décision consciente.Il existe des commandes générant une sortie qui doivent être évaluées (ce qui inclut le fractionnement, la globalisation et plus encore) et exécutées par le shell, c'est donc
eval "$(stuff)"
une possibilité (voir cette réponse ). Je n'ai jamais vu une commande qui a besoin de sa sortie pour subir un fractionnement + globalisation supplémentaire avant d'être imprimée . L'utilisation délibéréeecho $(stuff)
semble très rare.Et alors
var=$(stuff); echo "$var"
?Bon point. Cet extrait:
devrait être équivalent à
echo "$(stuff)"
équivalent (-ish *) àstuff
. Si c'est tout le code, lancez-le à lastuff
place.Si, toutefois, vous devez utiliser la sortie de
stuff
plusieurs fois, cette approcheest généralement mieux que
Même si
foo
c'est le casecho
et que vous obtenezecho "$var"
votre code, il peut être préférable de le conserver de cette façon. Choses à considérer:var=$(stuff)
stuff
courses une fois; même si la commande est rapide, éviter de calculer deux fois la même sortie est la bonne chose. Ou peut-êtrestuff
a des effets autres que l'écriture sur stdout (par exemple la création d'un fichier temporaire, le démarrage d'un service, le démarrage d'une machine virtuelle, la notification d'un serveur distant), de sorte que vous ne voulez pas l'exécuter plusieurs fois.stuff
génère une sortie temporelle ou quelque peu aléatoire, vous pouvez obtenir des résultats incohérents à partir defoo "$(stuff)"
etbar "$(stuff)"
. Après quevar=$(stuff)
la valeur de$var
est fixée, vous pouvez être sûrfoo "$var"
etbar "$var"
obtenir un argument de ligne de commande identique.Dans certains cas, au lieu de
foo "$var"
vous pouvez vouloir (besoin) d'utiliserfoo $var
, en particulier sistuff
génère plusieurs arguments pourfoo
(une variable de tableau peut être meilleure si votre shell le prend en charge). Encore une fois, sachez ce que vous faites. En ce qui concerneecho
la différence entreecho $var
etecho "$var"
est le même qu'entreecho $(stuff)
etecho "$(stuff)"
.* Équivalent ( -ish )?
J'ai dit
echo "$(stuff)"
est équivalent (-ish) àstuff
. Il y a au moins deux problèmes qui ne le rendent pas exactement équivalent:$(stuff)
s'exécutestuff
dans un sous-shell, il est donc préférable de direecho "$(stuff)"
est équivalent (-ish) à(stuff)
. Les commandes qui affectent le shell dans lequel elles s'exécutent, si elles sont dans un sous-shell, n'affectent pas le shell principal.Dans cet exemple,
stuff
voicia=1; echo "$a"
:Comparez-le avec
et avec
Un autre exemple, commencez par
stuff
êtrecd /; pwd
:et test
stuff
et(stuff)
versions.echo
n'est pas un bon outil pour afficher des données non contrôlées . Ce dontecho "$var"
nous parlions aurait dû êtreprintf '%s\n' "$var"
. Mais comme la question mentionneecho
et que la solution la plus probable est de ne pas l'utiliserecho
en premier lieu, j'ai décidé de ne pas introduireprintf
jusqu'à présent.stuff
ou(stuff)
entrelacera les sorties stdout et stderr, tandis queecho $(stuff)
toutes les sorties stderr destuff
(qui s'exécute en premier) seront imprimées , et alors seulement la sortie stdout digérée parecho
(qui s'exécutera en dernier).$(…)
supprime toute nouvelle ligne de fin, puis l'echo
ajoute en arrière.echo "$(printf %s 'a')" | xxd
Donne donc une sortie différente deprintf %s 'a' | xxd
.Certaines commandes (
ls
par exemple) fonctionnent différemment selon que la sortie standard est une console ou non; ills | cat
n'en va pas de mêmels
. De mêmeecho $(ls)
fonctionnera différemment quels
.Mis à
ls
part, dans un cas général, si vous devez forcer cet autre comportement,stuff | cat
c'est mieux queecho $(ls)
ouecho "$(ls)"
parce qu'il ne déclenche pas tous les autres problèmes mentionnés ici.Peut-être un statut de sortie différent (mentionné pour l'exhaustivité de cette réponse wiki; pour plus de détails, voir une autre réponse qui mérite le crédit).
la source
stuff
chemin entrelacerastdout
et sortirastderr
, tandis queecho `stuff`
toutes lesstderr
sorties seront impriméesstuff
et seulement ensuite lastdout
sortie.echo $(stuff)
peut vous poser bien plus de problèmes commeecho "$(stuff)"
si vous ne vouliez pas explicitement la globalisation et l'expansion.$(…)
supprime toute nouvelle ligne de fin, puis l'echo
ajoute à nouveau.echo "$(printf %s 'a')" | xxd
Donne donc une sortie différente deprintf %s 'a' | xxd
.ls
par exemple) fonctionnent différemment selon que la sortie standard est une console ou non; ills | cat
n'en va pas de mêmels
. Et bien sûrecho $(ls)
, fonctionnera différemment quels
.Autre différence: le code de sortie du sous-shell est perdu, donc le code de sortie de
echo
est récupéré à la place.la source
$?
sera le code retour deecho
(pourecho $(stuff)
) au lieu de celuireturn
édité parstuff
. Lastuff
fonctionreturn 1
(code de sortie), mais laecho
définit sur "0" quel que soit le code retour destuff
. Devrait encore être dans la réponse.