Ich habe eine der folgenden verwendet
echo $(stuff)
echo `stuff`
(wo stuff
ist zB pwd
oder date
oder etwas komplizierter).
Dann wurde mir gesagt, diese Syntax sei falsch, eine schlechte Praxis, nicht elegant, übertrieben, überflüssig, übermäßig kompliziert, eine Frachtkult-Programmierung, noobisch, naiv usw.
Aber der Befehl funktioniert , also was genau ist daran falsch?
shell-script
sh
echo
Kamil Maciorowski
quelle
quelle
echo $(echo $(echo $(echo$(echo $(stuff)))))
auch tut Arbeit, und immer noch würden Sie wahrscheinlich nicht verwenden, nicht wahr? ;-)stuff
. : DAntworten:
tl; dr
Eine Sohle
stuff
würde höchstwahrscheinlich für Sie arbeiten.Volle Antwort
Was geschieht
Wenn Sie rennen
foo $(stuff)
, passiert Folgendes:stuff
läuft;$(stuff)
beim Aufruf vonfoo
;foo
dann ausgeführt wird, hängen seine Befehlszeilenargumente offensichtlich davon ab, wasstuff
zurückgegeben wurde.Dieser
$(…)
Mechanismus wird "Befehlssubstitution" genannt. In Ihrem Fall ist der Hauptbefehl,echo
der im Grunde seine Befehlszeilenargumente an stdout ausgibt. Was auch immerstuff
versucht, auf stdout zu drucken, wird erfasst, an stdout übergebenecho
und von auf stdout gedrucktecho
.Wenn Sie möchten, dass die Ausgabe von
stuff
auf stdout gedruckt wird, führen Sie einfach die Sohle ausstuff
.Die
`…`
Syntax dient demselben Zweck wie$(…)
(unter demselben Namen: "Befehlssubstitution"), es gibt jedoch nur wenige Unterschiede, sodass Sie sie nicht blind austauschen können. Siehe diese FAQ und diese Frage .Sollte ich
echo $(stuff)
egal was vermeiden ?Es gibt einen Grund, den Sie verwenden möchten,
echo $(stuff)
wenn Sie wissen, was Sie tun. Aus dem gleichen Grund sollten Sie vermeiden,echo $(stuff)
wenn Sie nicht wirklich wissen, was Sie tun.Der Punkt ist
stuff
undecho $(stuff)
sind nicht genau gleichwertig. Letzteres bedeutet, dass der Operator split + glob für die Ausgabe vonstuff
mit dem Standardwert von aufgerufen wird$IFS
. Doppelte Anführungszeichen für die Befehlsersetzung verhindern dies. Wenn die Befehlsersetzung in einfache Anführungszeichen gesetzt wird, handelt es sich nicht mehr um eine Befehlsersetzung.Um dies beim Aufteilen zu beobachten, führen Sie die folgenden Befehle aus:
Und zum Verschlingen:
Wie Sie sehen,
echo "$(stuff)"
ist äquivalent (-ish *) zustuff
. Sie könnten es benutzen, aber was bringt es, die Dinge auf diese Weise zu komplizieren?Auf der anderen Seite , wenn Sie wollen die Ausgabe von
stuff
Splitting + zu unterziehen Globbing dann Sie können findenecho $(stuff)
nützlich. Es muss jedoch Ihre bewusste Entscheidung sein.Es gibt Befehle, die eine Ausgabe generieren, die ausgewertet werden sollte (einschließlich Teilen, Verschieben und mehr) und von der Shell ausgeführt
eval "$(stuff)"
wird. Dies ist eine Möglichkeit (siehe diese Antwort ). Ich habe noch nie einen Befehl gesehen, dessen Ausgabe vor dem Drucken einer zusätzlichen Aufteilung + Globe unterzogen werden muss . Bewusstes Verwendenecho $(stuff)
scheint sehr ungewöhnlich.Was ist
var=$(stuff); echo "$var"
?Guter Punkt. Dieser Ausschnitt:
sollte äquivalent sein zu
echo "$(stuff)"
äquivalent (-ish *) zustuff
. Wenn es sich um den gesamten Code handelt, führen Sie ihnstuff
stattdessen aus.Wenn Sie die Ausgabe jedoch
stuff
mehrmals verwenden müssen, verwenden Sie diesen Ansatzist normalerweise besser als
Selbst wenn dies der Fall
foo
istecho
und Sieecho "$var"
in Ihren Code aufgenommen werden, ist es möglicherweise besser, dies so zu belassen. Dinge, die man beachten muss:var=$(stuff)
stuff
läuft einmal; Selbst wenn der Befehl schnell ist, ist es das Richtige, zu vermeiden, dass dieselbe Ausgabe zweimal berechnet wird. Oder hat möglicherweisestuff
andere Auswirkungen als das Schreiben in stdout (z. B. Erstellen einer temporären Datei, Starten eines Dienstes, Starten einer virtuellen Maschine, Benachrichtigen eines Remoteservers), sodass Sie ihn nicht mehrmals ausführen möchten.stuff
zeitabhängige oder etwas zufällige Ausgaben generiert werden, erhalten Sie möglicherweise inkonsistente Ergebnisse vonfoo "$(stuff)"
undbar "$(stuff)"
. Nachvar=$(stuff)
dem Wert$var
festgelegt ist , und Sie können sicher sein ,foo "$var"
undbar "$var"
identisches Befehlszeilenargument bekommen.In einigen Fällen
foo "$var"
möchten (müssen) Sie möglicherweise anstelle von verwendenfoo $var
, insbesondere wenn mehrere Argumente fürstuff
generiert werden (eine Array-Variable ist möglicherweise besser, wenn Ihre Shell dies unterstützt). Weißt du nochmal, was du tust? Wenn es um den Unterschied zwischen und geht, ist er der gleiche wie zwischen und .foo
echo
echo $var
echo "$var"
echo $(stuff)
echo "$(stuff)"
* Äquivalent ( -ish )?
Ich sagte
echo "$(stuff)"
ist gleichbedeutend mitstuff
. Es gibt mindestens zwei Probleme, die es nicht genau gleichwertig machen:$(stuff)
läuftstuff
es in einer Subshell, so besser zu sagen ,echo "$(stuff)"
entspricht (ish) zu(stuff)
. Befehle, die sich auf die Shell auswirken, in der sie ausgeführt werden, wirken sich in einer Subshell nicht auf die Hauptshell aus.In diesem Beispiel
stuff
ista=1; echo "$a"
:Vergleichen Sie es mit
und mit
Beginnen Sie mit
stuff
einem anderen Beispielcd /; pwd
:und Test
stuff
und(stuff)
Versionen.echo
ist kein gutes Werkzeug, um unkontrollierte Daten anzuzeigen . Das,echo "$var"
worüber wir gesprochen haben, hätte sein sollenprintf '%s\n' "$var"
. Aber da die Frage erwähntecho
und die wahrscheinlichste Lösung darin besteht, sie überhaupt nicht zu verwendenecho
, habe ich mich entschlossen,printf
sie bis jetzt nicht vorzustellen .stuff
oder(stuff)
verschachteltecho $(stuff)
die Ausgabe von stdout und stderr, während die gesamte Ausgabe von stderrstuff
(die zuerst ausgeführt wird) und erst dann die Ausgabe von stdout gedruckt wird, die vonecho
(die zuletzt ausgeführt wird) verdaut wurde .$(…)
Entfernt alle nachfolgenden Zeilenumbrüche undecho
fügt sie dann wieder hinzu. Soecho "$(printf %s 'a')" | xxd
ergibt sich eine andere Ausgabe alsprintf %s 'a' | xxd
.Einige Befehle (
ls
zum Beispiel) funktionieren unterschiedlich, je nachdem, ob die Standardausgabe eine Konsole ist oder nicht. sols | cat
tut es nicht dasselbels
. Ähnlichecho $(ls)
wird anders funktionieren alsls
.Putting
ls
in einem allgemeinen Fall beiseite, wenn Sie das andere Verhalten zu zwingen , haben dannstuff | cat
als besserecho $(ls)
oderecho "$(ls)"
weil es alle anderen Themen erwähnt hier nicht ausgelöst hat.Möglicherweise abweichender Exit-Status (zur Vervollständigung dieser Wiki-Antwort genannt; für Details siehe eine andere Antwort , die Anerkennung verdient).
quelle
stuff
Weg wird verschachteltstdout
undstderr
ausgegeben, währendecho `stuff`
die gesamtestderr
Ausgabe vonstuff
und erst dann diestdout
Ausgabe gedruckt wird .echo $(stuff)
Sie können in weitaus mehr Schwierigkeiten geraten, alsecho "$(stuff)"
wenn Sie nicht explizit auf Globbing und Expansion verzichten möchten.$(…)
kleiner Unterschied: Entferne alle abschließenden Zeilenumbrüche und füge sie dann wiederecho
hinzu. Soecho "$(printf %s 'a')" | xxd
ergibt sich eine andere Ausgabe alsprintf %s 'a' | xxd
.ls
zum Beispiel) unterschiedlich funktionieren, je nachdem, ob die Standardausgabe eine Konsole ist oder nicht. sols | cat
tut es nicht dasselbels
. Und natürlichecho $(ls)
wird das anders funktionieren alsls
.Ein weiterer Unterschied: Der Exit-Code der Sub-Shell geht verloren, sodass
echo
stattdessen der Exit-Code von abgerufen wird.quelle
$?
der Rückgabecode vonecho
(forecho $(stuff)
) anstelle des vonreturn
ed sein wirdstuff
. Diestuff
Funktionreturn 1
(Exit-Code),echo
setzt sie aber unabhängig vom Return-Code von auf "0"stuff
. Sollte trotzdem in der Antwort stehen.