Unterschied zwischen 'ls' und 'echo $ (ls)'

27

Betrachten Sie die beiden Shell-Beispiele

$ ls
myDoc.html
SomeDirectory
someDoc.txt

und

$ echo $(ls)
myDoc.html SomeDirectory someDoc.txt

Der erste lsBefehl wird ausgeführt und hängt, wie ich weiß, den Inhalt des aktuellen Arbeitsverzeichnisses an die stdoutDatei an (was das Terminal anzeigt). Ist das richtig?

Der zweite lsBefehl übernimmt den Wert des Befehls (dh den Inhalt des aktuellen Arbeitsverzeichnisses) und druckt ihn in die stdoutDatei. Ist das richtig?

Warum geben die beiden Befehle unterschiedliche Ausgaben aus?

Owen
quelle
1
weil du echo machst. Die Ausgabe von ls als Eingabe für den Echo-Befehl.
Ankidaemon
Dies ist "erwartetes" Verhalten, das jemand in Kürze erklären wird. Sind Sie sich jedoch sicher, dass lsSie nicht mehrere Artikel pro Zeile erhalten?
Roaima
@roaima columnized ls ist ein Überbleibsel eines BSD-Features. Unix-Versionen drucken einen Eintrag pro Zeile
Steve Cox
@SteveCox Ich habe seit Anfang SVR4 kein richtiges UNIX mehr verwendet, daher ist mein Gedächtnis ein wenig verschwommen. Danke für die Erinnerung.
Roaima
Warum nicht echo *?
jdh8

Antworten:

70

Wenn Sie diesen Befehl ausführen:

ls

Das Terminal zeigt die Ausgabe von an ls.

Wenn Sie diesen Befehl ausführen:

echo $(ls)

Die Shell erfasst die Ausgabe von $(ls)und führt eine Wortteilung durch . Standardmäßig IFSbedeutet dies, dass alle Folgen von Leerzeichen, einschließlich Zeilenumbrüchen, durch ein einzelnes Leerzeichen ersetzt werden. Deshalb echo $(ls)erscheint die Ausgabe von in einer Zeile.

Weitere Informationen zur Wortteilung finden Sie in den häufig gestellten Fragen zu Greg .

Unterdrückung der Wortteilung

Die Shell führt keine Wortteilung für Zeichenfolgen in Anführungszeichen durch. So können Sie die Wortteilung unterdrücken und die mehrzeilige Ausgabe beibehalten mit:

echo "$(ls)"

ls und mehrzeilige Ausgabe

Sie haben das vielleicht bemerkt ls manchmal mehr als eine Datei pro Zeile gedruckt wird:

$ ls
file1  file2  file3  file4  file5  file6

Dies ist die Standardeinstellung, wenn die Ausgabe von lsan ein Terminal gesendet wird. Wenn der Ausgang nicht direkt an ein Terminal geht,ls wird der Standardwert in eine Datei pro Zeile geändert:

$ echo "$(ls)"
file1
file2
file3
file4
file5
file6

Dieses Verhalten ist in dokumentiert man ls.

Eine weitere Subtilität: Befehlsersetzung und nachfolgende Zeilenumbrüche

$(...)ist die Befehlsersetzung, und die Shell entfernt abschließende Zeilenumbrüche aus der Ausgabe der Befehlsersetzung . Dies ist normalerweise nicht erkennbar, da standardmäßig echoeine neue Zeile am Ende der Ausgabe eingefügt wird. Wenn Sie also eine neue Zeile am Ende von verlieren und eine davon $(...)gewinnen echo, gibt es keine Änderung. Wenn die Ausgabe Ihres Befehls jedoch mit 2 oder mehr Zeilenumbrüchen endet, während echonur eines zurückaddiert wird, fehlen in der Ausgabe eine oder mehrere Zeilenumbrüche. Als Beispiel können wir printfnachfolgende Zeilenumbrüche verwenden. Beachten Sie, dass beide der folgenden Befehle trotz der unterschiedlichen Anzahl von Zeilenumbrüchen dieselbe Ausgabe einer Leerzeile erzeugen:

$ echo "$(printf "\n")"

$ echo "$(printf "\n\n\n\n\n")"

$ 

Dieses Verhalten ist in dokumentiert man bash.

Eine weitere Überraschung: die doppelte Erweiterung der Pfadnamen

Lassen Sie uns drei Dateien erstellen:

$ touch 'file?' file1 file2

Beachten Sie den Unterschied zwischen ls file?und echo $(ls file?):

$ ls file?
file?  file1  file2
$ echo $(ls file?)
file? file1 file2 file1 file2

Im Fall von echo $(ls file?)wird der Dateiglob zweimalfile? erweitert , wodurch die Dateinamen und zweimal in der Ausgabe erscheinen. Dies liegt daran, dass, wie Jeffiekins betont, die Pfadnamenerweiterung zuerst von der Shell ausgeführt wird, bevor sie ausgeführt wird, und dann erneut, bevor sie ausgeführt wird.file1file2lsecho

Die zweite Pfadnamenerweiterung kann unterdrückt werden, wenn wir doppelte Anführungszeichen verwenden:

$ echo "$(ls file?)"
file?
file1
file2
John1024
quelle
4
zusätzlich, indem isattySie überprüfen , ob stdout ein tty ist, verwendet ls dies fest , ob die Farben oder nicht zu ermitteln.
Janus Troelsen
1
Stimmen die Anführungszeichen im letzten Codeausschnitt nicht überein? Oder bieten Sie $( )tatsächlich eine Syntaxbarriere, damit Sie nicht interpolieren müssen?
Katze
6
@cat $()bietet eine Syntaxbarriere.
Random832
2
Ein weiterer wichtiger Unterschied: Wenn ein Dateiname ein Platzhalterzeichen enthält, erweitert der echoBefehl das Platzhalterzeichen . Zum Beispiel, wenn lsdie Datei zurückgegeben wird? Datei1 Datei2 , dann echo $(ls)wird die Datei zurückgegeben? Datei1 Datei2 Datei1 Datei2 .
Jeffiekins
1
@ Jeffiekins echoerweitert keine Platzhalter; Die Shell führt dies aus, bevor die resultierende Erweiterung als Argumente an übergeben wird echo.
Chepner
0

ls ist sich bewusst, ob eine Ausgabe an ein Terminal gesendet wird oder nicht.

Das Ausführen lsan einer Eingabeaufforderung schreibt in Ihre Pseudotty. Das Umleiten der Ausgabe von lswird im Allgemeinen nicht zu einer Pseudo-Tty führen und lsdie Ausgabe anders formatieren.

mpez0
quelle