Erzeugt das Bash-Sternchen *-Platzhalterzeichen immer eine (aufsteigend) sortierte Liste?

Erzeugt das Bash-Sternchen *-Platzhalterzeichen immer eine (aufsteigend) sortierte Liste?

Ich habe ein Verzeichnis voller Dateien mit Namen wie: logXXwobei XX eine zweistellige, mit Nullen aufgefüllte Hex-Zahl in Großbuchstaben ist, wie etwa:

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

Im Allgemeinen sind es insgesamt weniger als 20 oder 30 Dateien. Datum und Uhrzeit sind auf meinem System nicht verlässlich (ein eingebettetes System ohne zuverlässige NTP- oder GPS-Zeitquellen). Die Dateinamen werden jedoch zuverlässig hochgezählt, wie oben gezeigt.

Ich möchte grepalle Dateien nach dem aktuellsten Protokolleintrag eines bestimmten Typs durchsuchen und dabei catdie Dateien wie folgt zusammenfassen:

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

Mir ist jedoch aufgefallen, dass in verschiedenen Versionen von bashoder shoder zshusw. möglicherweise unterschiedliche Vorstellungen davon vorliegen, wie das *erweitert wird.

Auf der man bashSeite steht nicht, ob die Erweiterung *eine definitiv aufsteigende alphabetische Liste übereinstimmender Dateinamen wäre. Sie scheint jedes Mal aufsteigend zu sein, wenn ich es auf allen mir zur Verfügung stehenden Systemen ausprobiert habe – aber ist das DEFINIERTES Verhalten oder nur implementierungsspezifisch?

Mit anderen Worten: Kann ich mich absolut darauf verlassen, cat /tmp/logs/log*dass alle meine Protokolldateien in alphabetischer Reihenfolge aneinandergereiht werden?

Antwort1

In allen Shells werden Globs standardmäßig sortiert.Sie waren schon beim /etc/globHelferwurde von der Shell von Ken Thompson aufgerufen, um Globs in der ersten Unix-Version in den frühen 70er Jahren zu erweitern (und gab den Globs ihren Namen).

Für sherfordert POSIX, dass sie mittels sortiert werden strcoll(), das heißt, unter Verwendung der Sortierreihenfolge im Gebietsschema des Benutzers, wie für , lsobwohl einige dies immer noch mittels tun strcmp(), das nur auf Byte-Werten basiert.

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log① log② lóg01
$ bash -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log②  log①  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log②
log①
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

Sie werden vielleicht oben bemerkt haben, dass bei Shells, die auf der Grundlage des Gebietsschemas sortieren, hier auf einem GNU-System mit einem en_GB.UTF-8Gebietsschema, das -in den Dateinamen beim Sortieren ignoriert wird (die meisten Satzzeichen würden das tun). Das ówird auf eine eher erwartete Weise sortiert (zumindest für Briten), und die Groß-/Kleinschreibung wird ignoriert (außer bei der Entscheidung über Gleichstände).

Sie werden jedoch einige Inkonsistenzen für log① log② bemerken. Das liegt daran, dass die Sortierreihenfolge von ① und ② in GNU-Gebietsschemata nicht definiert ist (derzeit; hoffentlich wird dies eines Tages behoben). Sie sortieren gleich, sodass Sie zufällige Ergebnisse erhalten.

Das Ändern des Gebietsschemas wirkt sich auf die Sortierreihenfolge aus. Sie können das Gebietsschema auf C setzen, um eine strcmp()-ähnliche Sortierung zu erhalten:

$ bash -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log① log② lóg01

Beachten Sie, dass einige Locales sogar bei reinen ASCII-Zeichenfolgen mit reinen Zahlen Verwirrung stiften können. Wie tschechische Locales (zumindest auf GNU-Systemen), wo cheinSortierelementdas sortiert nach h:

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

Oder, wie @ninjalj anmerkt, noch seltsamere in ungarischen Sprachen:

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

In zshkönnen Sie die Sortierung mit wählenGlob-Qualifikation. Zum Beispiel:

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

Die numerische Sortierung echo *(n)kann auch global mit der Option aktiviert werden numericglobsort:

$ zsh -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log① log② log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

Wenn Sie (wie ich) durch diese Reihenfolge in diesem konkreten Fall verwirrt sind (hier unter Verwendung meiner britischen Lokalisierung), sieheHierfür Details.

Antwort2

Auf der Manpage für Bash ist Folgendes angegeben:

Pfadnamenerweiterung

-fSofern die Option nicht gesetzt wurde, durchsucht bash nach der Worttrennung jedes Wort nach den Zeichen *, ?, und [. Wenn eines dieser Zeichen vorkommt, wird das Wort als Muster betrachtet und durch eine alphabetisch sortierte Liste von Dateinamen ersetzt, die dem Muster entsprechen […].

Antwort3

Sofern Sie in einigen Shells nicht ganz bestimmte Shell-Optionen auslösen, ist die Ausgabe garantiert dieselbe.

Die Reihenfolge ist festgelegt inder POSIX-Standard:

Wenn das Muster mit vorhandenen Dateinamen oder Pfadnamen übereinstimmt, wird das Muster durch diese Dateinamen und Pfadnamen ersetzt.sortiert nach der Sortierreihenfolge, die im aktuellen Gebietsschema gültig ist. Wenn diese Sortierreihenfolge nicht die vollständige Sortierung aller Zeichen enthält (siehe XBD LC_COLLATE), sollten alle Dateinamen oder Pfadnamen, die gleich sortiert werden, Byte für Byte weiter verglichen werden, wobei die Sortierreihenfolge für das POSIX-Gebietsschema verwendet wird.

Siehe auchLC_COLLATE-Kategorie im POSIX-Gebietsschema, was kurz gesagt bedeutet, dass wenn LC_COLLATE=C, dann die Dinge in ASCII-Reihenfolge sortiert werden.


Das bashHandbuch erwähnt

LC_COLLATE

Diese Variable bestimmt die Sortierreihenfolge, die beim Sortieren der Ergebnisse der Pfadnamenerweiterung verwendet wird, und bestimmt das Verhalten von Bereichsausdrücken, Äquivalenzklassen und Sortiersequenzen innerhalb der Pfadnamenerweiterung und des Mustervergleichs.

ksh93und zshhat einen ähnlichen Wortlaut, was mich annehmen lässt, dass sie in dieser Hinsicht dem POSIX-Standard folgen.

Andere Shells wie pdkshund dashsagen nichts über die Sortierung der Dateinamen aus, die sich aus dem Dateinamen-Globbing ergibt. Ich bin geneigt zu glauben, dass dies bedeutet, dass sie immer noch denselben Standard einhalten, zumindest bei Verwendung des POSIX-Gebietsschemas. Meiner Erfahrung nach ist mir noch keine Shell begegnet, die eine offenkundig „seltsame“ Sortierung von ASCII-Dateinamen vornimmt.

Antwort4

Wenn das Hauptziel darin besteht, Eingabedateien nach ihrem Alter zu sortieren (die ältesten zuerst), könnten Sie schreiben

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

Und wenn auch rotierte und komprimierte Protokolle beteiligt sind:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever

verwandte Informationen