Die Ausgabe von
# ls
und
# echo $(ls)
sind gleich. Was genau bedeuten das $ -Zeichen und die Klammer?
Was passiert auf technischer Ebene, wenn die Ausgabe dieser beiden Befehle gleich ist?
Die Ausgabe von
# ls
und
# echo $(ls)
sind gleich. Was genau bedeuten das $ -Zeichen und die Klammer?
Was passiert auf technischer Ebene, wenn die Ausgabe dieser beiden Befehle gleich ist?
Antworten:
Dies ist die Befehlsersetzung für bash.
Durch die Befehlsersetzung kann die Ausgabe eines Befehls den Befehl selbst ersetzen. Die Befehlsersetzung erfolgt, wenn ein Befehl wie folgt eingeschlossen ist:
$(command) or `command`
http://www.gnu.org/software/bash/manual/bashref.html#Command-Substitution
Mehr:
http://tldp.org/LDP/abs/html/commandsub.html
http://wiki.bash-hackers.org/syntax/expansion/cmdsubst
Und ein gutes Beispiel ähnlich Ihrer Frage:
http://bashshell.net/shell-scripts/using-command-substitution-in-a-bash-shell-script/
Wenn die Shell auf eingeschlossenen Text stößt $(
)
, gilt Folgendes:
$(...)
$(...)
Wie die Antwort von heartmagic erklärt, ist dies eine von zwei verfügbaren Syntaxen für die Befehlssubstitution .
Es ist eigentlich etwas ungewöhnlich , dass der Ausgang ls
sein genau das gleiche wie die Ausgabe von echo $(ls)
:
ek@Io:~/tmp$ ls
bar foo
ek@Io:~/tmp$ echo $(ls)
bar foo
ls
Trennt Dateinamen normalerweise durch zwei oder mehr Leerzeichen oder eine neue Zeile. Dies hilft uns, sie leichter voneinander zu unterscheiden, insbesondere da Leerzeichen in Dateinamen etwas häufig vorkommen (aber mehrere aufeinanderfolgende Leerzeichen, weniger häufig).
Als ich rannte echo $(ls)
, passierte Folgendes.
Befehlsersetzung ersetzt$(ls)
durch:
bar
foo
Sie werden überrascht sein, das zu hören, da Sie das wahrscheinlich nicht sehen, wenn Sie ls
alleine laufen ! Diese Ungleichheit bei den ls
Ausgaben wird im Folgenden erläutert, ist jedoch nicht der Grund, warum zwischen diesen beiden Wörtern nur ein Leerzeichen steht. Dasselbe würde passieren, wenn $(ls)
es durch bar foo
(mit zwei Leerzeichen) ersetzt würde.
Anschließend führte die Shell eine Wortaufteilung durch , wobei der Befehl als separate Argumente behandelt wurde bar
und nicht foo
als einzelnes Argument, das Leerzeichen enthielt.echo
Globbing (aka Dateiname / Pfadnamenerweiterung) würde auch durchgeführt werden, wenn die Ausgabe des Befehls enthalten hatte *
, ?
oder [
.
Wie unten ausgeführt, ermöglicht das Zitieren mit doppelten Anführungszeichen das Ersetzen von Befehlen, unterdrückt jedoch das Aufteilen und Globbing von Wörtern.
Wenn echo
mehrere Argumente empfangen werden (mit Ausnahme von Optionen wie -n
, die speziell behandelt und überhaupt nicht gedruckt werden), werden sie alle mit einem einzigen Leerzeichen zwischen den nachfolgenden Argumenten ausgedruckt. echo
druckt dann eine neue Zeile, sofern die -n
Option nicht übergeben wurde.
So echo $(ls)
zeigt Ausgabe wie bar foo
statt bar foo
.
Wenn Sie laufen ls
und listet keine Dateien oder nur eine Datei, die Ausgabe des ls
Willens oft die gleiche sein wie die Ausgabe von echo $(ls)
. Selbst dann ist es nicht immer dasselbe, z. B. wenn ein Dateiname andere Leerzeichen als isolierte (einzelne) Leerzeichen enthält:
ek@Io:~/tmp2$ touch 'my file'
ek@Io:~/tmp2$ ls
my file
ek@Io:~/tmp2$ echo $(ls)
my file
Wenn ls
mehrere Dateien gedruckt werden, entspricht die Ausgabe wahrscheinlich nicht genau der Ausgabe von $(ls)
. Ähnlich:
echo a b
und echo $(echo a b)
produzieren die gleiche Ausgabe, aberecho 'a b'
und echo $(echo 'a b')
nicht.Dies ist eine wichtige Sache, die Sie über die Befehlssubstitution wissen sollten. Nicht zitierte Befehlssubstitutionen unterliegen der Wortaufteilung (und dem Globbing) .
Wenn Sie verhindern möchten , dass einzelne Wörter aufgespalten und Globbing --und Sie in der Regel werden sie verhindern wollen - Sie Ihren Ausdruck für Kommandosubstitution in beilegen können doppelte Anführungszeichen ( "
"
). Der Grund für die Verwendung von doppelten Anführungszeichen anstelle von einfachen Anführungszeichen ist, dass einfache Anführungszeichen noch stärker sind. Sie würden die Befehlsersetzung unterdrücken.
Dies gilt auch für die Parametererweiterung , die eine häufiger verwendete Shell-Erweiterung als die Befehlssubstitution ist, und für die arithmetische Erweiterung . Die Gründe, warum es wichtig ist, Befehlsersetzungen anzugeben (außer in dem eher ungewöhnlichen Fall), dass Sie wissen , dass weitere Erweiterungen auftreten sollen, sind dieselben wie die Gründe für die Angabe der Parametererweiterung .
Doppelte Anführungszeichen reichen oft aus, um unerwartete Ergebnisse bei der Befehlssubstitution zu vermeiden. Insbesondere funktioniert es mit den echo
obigen Beispielen sowie dem Beispiel ls
eines Verzeichnisses mit einem einzelnen Eintrag, der Leerzeichen enthält:
ek@Io:~/tmp2$ echo "$(ls)"
my file
Jedoch, wie oben erwähnt, könnten Sie überrascht feststellen , dass das, was Sie sehen , wenn Sie laufen ls
oft nicht der Fall ist , was übergeben wird echo
von an Ort und Stelle "$(ls)"
:
ek@Io:~/tmp$ ls
bar foo
ek@Io:~/tmp$ echo "$(ls)"
bar
foo
Dies liegt daran, dass ls
überprüft wird, ob die Standardausgabe ein Terminal ist , um zu entscheiden, wie die Ausgabe formatiert werden soll, wenn die Ausgabeformatierung nicht explizit angegeben ist.
ls
Ausgaben in vertikal sortierten Spalten wie z ls -C
.ls
listet jeder Eintrag in einer eigenen Zeile auf, wie z ls -1
.Bei der Befehlssubstitution ist die Standardausgabe kein Terminal, da die Ausgabe des Befehls nicht direkt an ein Terminal gesendet wird, damit Sie sie sehen können. Stattdessen wird sie von der Shell erfasst und als Teil eines anderen Befehls verwendet.
ls
Übergeben Sie das -C
Flag , um eine mehrspaltige Formatierung beim Ausführen über die Befehlsersetzung zu erhalten :
ek@Io:~/tmp$ echo "$(ls -C)"
bar foo
( dir
wird manchmal als Alternative dazu vorgeschlagen ls -C
und funktioniert auch dafür, dir
verhält sich aber ls -C -b
eher wie als nurls -C
.)
Ein weiterer Grund ls
kann manchmal verhalten sich anders als echo "$(ls)"
ist , dass ls
sein kann ein Shell - Alias . Führen Sie aus alias ls
, um zu überprüfen; unter Ubuntu bekommt man normalerweise alias ls='ls --color=auto'
. Dies macht es so, dass, wenn es ls
als erstes Wort eines Befehls angezeigt wird, den Sie interaktiv ausführen (und auch in der weitaus selteneren Situation, dass die Alias-Erweiterung in einer nicht interaktiven Shell aktiviert wurde), es durch ersetzt wird ls --color=auto
.
Wenn ich zum Beispiel laufe ls
, werden Verzeichnisse aufgelistet, die blau und grün gefärbt sind (und es werden auch viele andere Farbregeln beachtet). --color=auto
bewirkt ls
, dass eine farbige Ausgabe gedruckt wird, wenn die Standardausgabe ein Terminal ist, und nicht anders.
Übergeben Sie die Option oder, um eine farbige Ausgabe beim Ausführen ls
über die Befehlsersetzung zu erhalten . Wenn Sie möchten, können Sie dies kombinieren mit :--color
--color=always
-C
echo $(ls -C --color)
Beachten Sie, dass Sie zwar ls
einen Alias für ls --color
oder ls --color=always
anstelle von erstellen ls --color=auto
können und ls --color=always
es egal ist, ob die Standardausgabe ein Terminal ist ... dieser Alias jedoch immer noch keine ls
farbige Ausgabe erzeugt, wenn er durch Befehlsersetzung aufgerufen wird. Dies liegt daran, dass Shell-Aliase (in Bourne-Shells wie bash ) nur erweitert werden, wenn die Shell sie als erstes Wort eines Befehls sieht und sie nicht von Subshells geerbt werden.
()
und $()
werden verwendet, um Befehle in einer Subshell auszuführen.
Weitere Informationen finden Sie unter http://tldp.org/LDP/abs/html/subshells.html .
(ls)
ist kein Variablenname.