warum listet ls -d auch dateien auf und wo ist das dokumentiert?

48
  • Bei der Angabe ls --directory a*sollten nur Verzeichnisse aufgeführt werden, die mit beginnena*
  • ABER es listet Dateien UND Verzeichnisse auf, die mit beginnen a

Fragen :

  • wo könnte ich eine Dokumentation dazu finden, außer manund infowo glaube ich, habe ich gründlich nachgesehen?
  • funktioniert das nur in BASH?
erch
quelle
1
Siehe auch diese andere sehr ähnliche Frage .
Stéphane Chazelas
5
echo a*listet Ihnen auch die Dateien auf, die mit a(falls vorhanden) beginnen. Nur um klarer zu machen, dass das nicht lsso ist, sondern die Bash.
ash108

Antworten:

89

Die Syntax a*und *a*wird von der Shell und nicht vom lsBefehl implementiert .

Wenn Sie tippen

ls a*

An der Shell-Eingabeaufforderung wird die Shell a*zu einer Liste aller im aktuellen Verzeichnis vorhandenen Dateien erweitert , deren Namen mit beginnen a. Beispielsweise kann es a*auf die Sequenz erweitert a1 a2 a3und diese als Argumente an übergeben werden ls. Der lsBefehl selbst sieht den *Charakter nie ; es sieht nur die drei Argumente a1, a2und a3.

Für die Platzhaltererweiterung bezieht sich "Dateien" auf alle Entitäten im aktuellen Verzeichnis. Dies kann beispielsweise a1eine normale Datei, a2ein Verzeichnis oder a3ein Symlink sein. Sie haben alle Verzeichniseinträge, und es ist der Platzhaltererweiterung der Shell egal, auf welche Art von Entität sich diese Einträge beziehen.

Praktisch alle Shells, auf die Sie wahrscheinlich stoßen (bash, sh, ksh, zsh, csh, tcsh, ...), implementieren Platzhalter. Die Details können variieren, aber die grundlegende Syntax des *Abgleichs von null oder mehr Zeichen und des ?Abgleichs eines einzelnen Zeichens ist einigermaßen konsistent.

Insbesondere für Bash ist dies im Abschnitt "Dateinamenerweiterung" des Bash-Handbuchs dokumentiert. starte info bashund suche nach "Dateinamenerweiterung", oder sieh hier .

Die Tatsache, dass dies von der Shell und nicht von einzelnen Befehlen ausgeführt wird, hat einige interessante (und manchmal überraschende) Konsequenzen. Das Beste daran ist, dass der Umgang mit Platzhaltern für (fast) alle Befehle konsistent ist . Wenn die Shell dies nicht tun würde, würden sich einige Befehle zwangsläufig nicht darum kümmern, und andere würden es auf subtile Weise tun, die der Autor für "besser" hielt. (Ich denke, die Windows-Befehlsshell hat dieses Problem, aber ich kenne es nicht gut genug, um weitere Kommentare abzugeben.)

Andererseits ist es schwierig, einen Befehl zum Umbenennen mehrerer Dateien zu schreiben. Wenn du schreibst:

mv *.log *.log.bak

Es wird wahrscheinlich fehlschlagen, da *.log.bakes basierend auf den Dateien, die bereits im aktuellen Verzeichnis vorhanden sind, erweitert wird. Es gibt Befehle, die so etwas tun, aber sie müssen ihre eigene Syntax verwenden, um anzugeben, wie die Dateien umbenannt werden sollen. Einige Befehle (z. B. find) können ihre eigene Platzhaltererweiterung ausführen . Sie müssen die Argumente angeben, um die Erweiterung der Shell zu unterdrücken:

find . -name '*.txt' -print

Die Platzhaltererweiterung der Shell basiert ausschließlich auf der Syntax des Befehlszeilenarguments und der Menge der vorhandenen Dateien. Es kann nicht durch die Bedeutung des Befehls beeinflusst werden. Wenn Sie beispielsweise alle .logDateien in das übergeordnete Verzeichnis verschieben möchten , können Sie Folgendes eingeben:

mv *.log ..

Wenn Sie das vergessen ..:

mv *.log

und es gibt zufällig genau zwei .logDateien im aktuellen Verzeichnis, es wird erweitert auf:

mv one.log two.log

die wird umbenennen one.logund clobber two.log.

EDIT : Und nach 52 Upvotes, einem Accept und einem Guru-Badge sollte ich vielleicht die Frage im Titel beantworten.

Die Option -doder sagt nicht, dass nur Verzeichnisse aufgelistet werden sollen. Es weist es an, Verzeichnisse nur als sich selbst aufzulisten, nicht als deren Inhalt. Wenn Sie einen Verzeichnisnamen als Argument angeben , wird standardmäßig der Inhalt des Verzeichnisses aufgelistet , da dies normalerweise für Sie von Interesse ist. Mit dieser Option wird festgelegt, dass nur das Verzeichnis selbst aufgelistet wird. Dies kann insbesondere in Kombination mit Platzhaltern hilfreich sein. Wenn Sie Folgendes eingeben:--directorylsls-d

ls -l a*

lsSie erhalten eine lange Liste aller Dateien, deren Name mit "" beginnt a, sowie der Inhalte aller Verzeichnisse, deren Name mit "" beginnt a. Wenn Sie nur eine Liste der Dateien und Verzeichnisse mit jeweils einer Zeile benötigen, können Sie Folgendes verwenden:

ls -ld a*

was äquivalent ist zu:

ls -l -d a*

Denken Sie erneut daran, dass der lsBefehl das *Zeichen nie sieht .

Wo dies dokumentiert ist, man lszeigt Ihnen die Dokumentation für den lsBefehl auf nahezu jedem Unix-ähnlichen System. Auf den meisten Linux-basierten Systemen ist der lsBefehl Teil des GNU-Pakets coreutils. wenn Sie die haben infoBefehl, entweder info lsoder info coreutils lssollten Sie mehr endgültige und umfassende Dokumentation geben. Andere Systeme, wie z. B. MacOS, verwenden möglicherweise andere Versionen des lsBefehls und verfügen möglicherweise nicht über den infoBefehl. Verwenden Sie für diese Systeme man ls. Und ls --helpzeigt eine relativ kurze Verwendungsmeldung (117 Zeilen auf meinem System), wenn Sie die GNU-Coreutils-Implementierung verwenden.

Und ja, auch Experten müssen ab und zu die Dokumentation konsultieren. Siehe auch diesen klassischen Witz .

Keith Thompson
quelle
8
@Thomas: Erweitert a*die Liste aller Dateien im aktuellen Verzeichnis, deren Namen mit beginnen a.
Keith Thompson
2
@Thomas: Oder in einem von Ihnen angegebenen Verzeichnis:ls subdir/a*
Keith Thompson
1
Um wirklich den Befehl zu sehen, der nach der Shell-Erweiterung ausgeführt wird, gibt echoes. Wenn Sie also wissen möchten, was passiert, wenn Sie dies tun ls a*, führen Sie zuerstecho ls a*
Carlos Campderrós
1
oder einfach nur echo a*... die Schale tut glob Expansion sowieso
Useless
2
@sendmoreinfo: Das hängt von der Shell und den Einstellungen ab. In csh und tcsh ist eine fehlgeschlagene Glob-Erweiterung ein Fehler. In Bash shopt -s failglobverursacht das gleiche Verhalten. Das Festlegen von, nullglobaber nicht, failglobführt beispielsweise *nosuchfile*dazu, dass eine leere Zeichenfolge angezeigt wird.
Keith Thompson
27

Siehe Keith Thompsons Antwort; Aber um zu erklären, warum ls --directory a*Dateien und Verzeichnisse --directoryangezeigt werden : Die Option unterdrückt keine Nicht-Verzeichnisdateien. Stattdessen listet es die Verzeichnisse als solche auf, während es sonst deren Inhalt auflisten würde. Beispiel:

$ mkdir foo
$ touch foo/bar
$ ls foo
bar
$ ls --directory foo
foo
Chirlu
quelle
3
-F
Dieses
6

Um sehr deutlich zu sein, ist es in der Manualpage ls (1) dokumentiert :

-d, --directory listet Verzeichniseinträge anstelle von Inhalten auf und dereferenziert keine symbolischen Links

um fair zu sein, "Einträge statt Inhalte" könnten wahrscheinlich erklärender sein:

Wenn FILE ein Verzeichnis ist, zeigen Sie den Verzeichniseintrag selbst an, anstatt den Inhalt dieses Verzeichnisses aufzulisten. Wenn DATEI eine symbolische Verknüpfung ist, wird der Verknüpfungseintrag selbst anstelle der Datei angezeigt, auf die die Verknüpfung verweist.

msw
quelle
Um pedantisch zu sein, obwohl die Option -d (wenn auch knapp) in der Manpage erklärt werden kann, sind die Argumenterweiterung und das Platzhalterzeichen in der Manpage von ls nicht dokumentiert, da dies von der Shell ausgeführt wird. Wenn Sie die Dateinamenerweiterung in Ihrer Shell deaktiviert haben, zeigt "ls a *" nur eine Datei mit dem wörtlichen Namen "a *" an.
Johnny
Um pedantisch zu sein, während die Shell-Erweiterung von anderen Befragten beantwortet wurde, geht keiner auf die spärliche Erklärung und die Frage ein, wie sie falsch verstanden werden könnte. Wenn Sie möchten, dass ich das wiederhole, was bereits gut gesagt wurde, gehen Sie bitte direkter vor.
msw
4

Globbing

Wie bereits erläutert, wird die Erweiterung von *(und ähnlichen Erweiterungen) globim Unix-Jargon als Bing bezeichnet und ist normalerweise eine Funktion des Befehlsprozessors (im Unix-Sprachgebrauch als "Shell" bekannt). Globbing kann also auch an vielen anderen Orten eingesetzt werden. Geben Sie man 7 globan der Shell einer klassischen Linux - Distribution (oder sehen diese ) , um mehr über globbing.

Wurde in frühen Unix- globVersionen von einem separaten Programm namens implementiert /etc/glob( Dokumentation dazu finden Sie auf Seite 10 dieses alten UNIX-Handbuchs ). Heutzutage ist es eine Code-Routine, die von Code-Bibliotheken bereitgestellt wird und üblicherweise von Shells verwendet wird. Quelle : Wikipedia .

Verzeichnisse

Warum werden sowohl ls -dDateien als auch Verzeichnisse aufgelistet?

Die lsManpage erklärt, warum dies passiert, aber mit einer sehr knappen Erklärung. Also hier ist ein Versuch einer erweiterten Erklärung:

Standardmäßig wird lsder Inhalt von Verzeichnissen aufgelistet, wenn ihr Name angegeben wird. Dies gilt auch für symbolische Verknüpfungen zu Verzeichnissen. Die -dOption bedeutet, dass "wenn der Name (die Namen) des Verzeichnisses (der Verzeichnisse) angegeben wird" (was auch implizit durch globBing angegeben werden kann) , nur die _Namen_s des Verzeichnisses (der Verzeichnisse) angezeigt werden, nicht jedoch deren Inhalt. Wenn Sie den oder die Namen von Symlinks zu Verzeichnissen (explizit oder implizit ) angeben , geben Sie den Dateinamen des Symlinks an , nicht den Inhalt des Verzeichnisses, auf das verwiesen wird.

Die -dOption hat, soweit ich das beurteilen kann, nichts damit zu tun, welche Artikel aufgelistet sind; (: Quelle kann dies getan werden hier ) mit find, etwa so: find . -maxdepth 1 -type d. Ich bin mir nicht sicher, ob es einen guten Weg gibt, dies nur mit GNU zu tun ls. Im Folgenden finden Sie einige Beispiele für die Verwendung des findBefehls.

Fazit: Standardmäßig lswird der Inhalt von Verzeichnissen angezeigt , wenn ihr Pfadname angegeben wird. -dÄndert dieses Verhalten, indem nur der Verzeichnisname selbst angezeigt wird, wie dies bei einer Standarddatei der Fall wäre.

Abbafei
quelle
3

Die Dokumentation gibt nicht an, dass nur Verzeichniseinträge aufgelistet werden, sondern dass beim lsEmpfangen des Verzeichnisnamens der Verzeichniseintrag anstelle des Inhalts aufgeführt wird. Der beste Weg, dies zu verstehen, ist durch ein Beispiel:

> {ice} ~ :10:47 % ls -l / 
total 97
drwxr-xr-x   2 root root  4096 May  3 00:27 bin
drwxr-xr-x   4 root root  1024 May  3 14:17 boot
drwxr-xr-x   2 root root  4096 Apr 29 13:44 cdrom
drwxr-xr-x  18 root root  4420 May  9 09:58 dev
...
lrwxrwxrwx   1 root root    33 May  3 14:16 vmlinuz -> boot/vmlinuz-3.9.0-030900-generic
lrwxrwxrwx   1 root root    29 May  3 11:07 vmlinuz.old -> boot/vmlinuz-3.8.0-19-generic
> {ice} ~ :10:47 % ls -ld /
drwxr-xr-x 25 root root 4096 May  3 14:16 /
defhlt
quelle
2

Die Dokumentation ist Teil der Shell . Insbesondere führt die Shell verschiedene Erweiterungen und Ersetzungen in einer bestimmten Reihenfolge durch, bevor ein Befehl ausgeführt wird.

Die Folge der Worterweiterungen für eine Bourne-kompatible Shell ist

  1. Tilde-Erweiterung
  2. Parametererweiterung
  3. Befehlsersetzung
  4. Arithmetische Erweiterung
  5. Feldaufteilung
  6. Pfadnamenerweiterung
  7. Angebot entfernen

Der lsBefehl sieht also nicht einmal die *Zeichen, die Argumente sind bereits mit allen Dateien erweitert, die mit dem a*Glob-Muster übereinstimmen . Dies ist anders als unter Windows, wo jeder Befehl, der das Globbing von Dateinamen benötigt, diese Funktion selbst implementieren muss.

Jens
quelle
1

Das Schlüsselwort, das Sie benötigen ( man bash), ist "Pathname Expansion".

Hauke ​​Laging
quelle
3
eher wie "Pathname Expansion" oder "Filename Generation". Bezogen auf "Pattern Matching", aber nicht dasselbe.
Stéphane Chazelas
@StephaneChazelas In der Tat, aber "Pattern Matching" ist ein Unterabschnitt von "Pathname Expansion" in der Manpage, während "Filename Generation" dort überhaupt nicht vorkommt (nur im Info-Dokument).
Hauke ​​Laging