Erzeugt der Platzhalter Bash Star * immer eine (aufsteigende) sortierte Liste?

53

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

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

Im Allgemeinen gibt es weniger als 20 oder 30 Dateien insgesamt. Das Datum und die Uhrzeit auf meinem bestimmten System können nicht als zuverlässig angesehen werden (ein eingebettetes System ohne zuverlässige NTP- oder GPS-Zeitquellen). Die Dateinamen werden jedoch wie oben gezeigt zuverlässig erhöht.

Ich wollte grepalle Dateien für den letzten einzelnen Protokolleintrag eines bestimmten Typs durchgehen, ich hatte gehofft, catdie Dateien zusammen wie ...

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

Mir ist jedoch aufgefallen, dass verschiedene Versionen von bashoder shoder zshusw. unterschiedliche Vorstellungen darüber haben, wie das *erweitert wird.

Die man bashSeite sagt nicht aus, ob die Erweiterung von *eine definitiv aufsteigende alphabetische Liste passender Dateinamen wäre oder nicht . Es scheint jedes Mal aufzusteigen, wenn ich es auf allen mir zur Verfügung stehenden Systemen ausprobiert habe - aber ist es 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 zusammengefügt werden?

Wossname
quelle
1
@ADDB Die Standardsortierreihenfolge für sortist dieselbe wie für die Shell, wenn ein Dateinamen-Globbing-Muster erweitert wird.
Kusalananda
9
Das ist eine schreckliche Praxis bei der Benennung von Dateien. Warum startest du deinen Lauf mit log (0) = - infty?
EP
14
@EP Unser Dateisystem ist ein komplexes 7-dimensionales Hyper-Toroid mit surrealer Nummerierung der Inodes. Es war großväterlich mit einem obskuren Zweig der Busybox und wir haben es jetzt nicht geschafft :)
Wossname
1
Sie können vermeiden , catmit grep -h pattern /tmp/logs/log*Voranstellen von Dateinamen zu den Spielen zu unterdrücken. (Zumindest mit GNU grep habe ich POSIX oder busybox nicht überprüft.)
Peter Cordes
1
@ Kusalananda Sie haben von nutzlosem Gebrauch von gehört cat, dies ist nutzlos vonsort
Katze

Antworten:

52

In allen Shells werden Globs standardmäßig sortiert. Sie wurden bereits von dem /etc/globHelfer von Ken Thompsons Shell aufgerufen, um Globs in der ersten Version von Unix in den frühen 70ern zu erweitern (und die Globs ihren Namen gaben).

Für shPOSIX ist es erforderlich strcoll(), dass die Sortierung anhand der Sortierreihenfolge im Gebietsschema des Benutzers erfolgt, wie dies bei lseinigen noch immer der Fall strcmp()ist und 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 bemerken, dass für die Shells, die nach dem Gebietsschema sortieren, hier auf einem GNU-System mit einem en_GB.UTF-8Gebietsschema, der -in den Dateinamen für die Sortierung ignoriert wird (die meisten Interpunktionszeichen würden). Die óSortierung erfolgt erwartungsgemäßer (zumindest für die Briten), und die Groß- und Kleinschreibung wird ignoriert (außer wenn es um die Entscheidung über eine Bindung geht).

Sie werden jedoch einige Inkonsistenzen für log① log② feststellen. Das liegt daran, dass die Sortierreihenfolge von ① und ② nicht in GNU-Gebietsschemata definiert ist (derzeit; hoffentlich wird sie eines Tages behoben). Sie sortieren gleich, so dass 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 Gebietsschemas auch für reine ASCII-Zeichenfolgen zu Verwirrungen führen können. Wie die tschechischen (zumindest auf GNU-Systemen), wo chsich ein Sortierelement befindet, das nach Folgendem sortiert h:

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

Oder, wie von @ninjalj hervorgehoben, noch seltsamere in ungarischen Gegenden:

$ 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 Glob-Qualifiern wählen . 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 numericglobsortOption aktiviert werden :

$ 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) in diesem speziellen Fall (hier unter Verwendung meines britischen Gebietsschemas) durch diese Anweisung verwirrt sind, finden Sie hier weitere Informationen.

Stéphane Chazelas
quelle
1
Der Fall 'ch' kann noch seltsamer sein: Einige Gebietsschemata können entscheiden, dass 'ch', 'Ch' und 'CH' jeweils ein Sortierelement sind, während 'cH' zwei Sortierelemente sind. Siehe: unicode.org/cldr/trac/ticket/889 Das aktuelle CLDR scheint nicht ganz konsistent zu sein: Das aktuelle Ungarisch ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) hat Regeln wie &C<cs<<<Cs<<<CS, während &C<cs<<<cS<<<Cs<<<CSals vorgeschlagener Versuchsentwurf gekennzeichnet ist. Nach einigen älteren Daten zu urteilen, die in CLDR importiert wurden, schienen ältere AIX- und MS-Systeme die Ansicht "Kleinbuchstaben sind 2 verschiedene Sortierelemente" vorzuziehen.
Ninjalj
Und ich habe Systeme gesehen, auf denen es sowieso nicht funktioniert hat. :(
Joshua
38

Die Manpage für bash gibt Folgendes an:

Pfadnamenerweiterung

Nach dem Wort Spaltung, es sei denn , die -fOption festgelegt wurde, scannt bash jedes Wort für die Charaktere *, ?und [. Wenn eines dieser Zeichen erscheint, wird das Wort als Muster angesehen und durch eine alphabetisch sortierte Liste von Dateinamen ersetzt, die dem Muster […] entsprechen.

user4556274
quelle
1
Habe gerade einen interessanten Fehler in Putty oder manbeim Rendern von Text gefunden ... Wenn der gesuchte Text "umbrochen" wird, wird er von einem / search-Befehl nicht gefunden. Nur mein Terminal maximiert und da ist es :)
Wossname
2
Du hast gedeckt bash. Das OP interessierte sich auch für "zsh etc."
Kusalananda
29

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

Die Bestellung ist im POSIX-Standard festgelegt :

Wenn das Muster mit vorhandenen Dateinamen oder Pfadnamen übereinstimmt, wird das Muster durch diese Dateinamen und Pfadnamen ersetzt, die nach der im aktuellen Gebietsschema gültigen Sortierreihenfolge sortiert sind . Wenn diese Sortierfolge nicht alle Zeichen in ihrer Gesamtreihenfolge enthält (siehe XBD LC_COLLATE), sollten alle Dateinamen oder Pfadnamen, die gleich sortiert sind, unter Verwendung der Sortierfolge für das POSIX-Gebietsschema weiter byteweise verglichen werden.

Siehe auch LC_COLLATE-Kategorie im POSIX-Gebietsschema , in dem kurz gesagt steht, dass wenn LC_COLLATE=C, die Dinge in ASCII-Reihenfolge sortiert sind.


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 Sortierfolgen innerhalb der Pfadnamenerweiterung und des Mustervergleichs.

ksh93und zshhat einen ähnlichen Wortlaut, der mich zu der Annahme veranlasst, dass sie in dieser Hinsicht dem POSIX-Standard folgen.

Andere Shells mögen pdkshund dashsagen nichts über die Sortierung der Dateinamen aus, die sich aus dem Verschieben von Dateinamen ergeben. Ich bin versucht zu glauben, dass dies bedeutet, dass sie immer noch denselben Standard einhalten, zumindest wenn sie das POSIX-Gebietsschema verwenden. Meiner Erfahrung nach bin ich nicht auf eine Shell gestoßen, die ASCII-Dateinamen "seltsam" sortiert.

Kusalananda
quelle
2
Sehen Sie sich die numericglobsortOption an zsh, die die Sortierung beeinflussen würde. Obwohl ich es vorziehen würde, echo *(n)die Option global zu aktivieren, anstatt sie global zu aktivieren .
Stéphane Chazelas
Ein Trottel. Bash ist im Standardmodus NICHT Posix-konform.
fpmurphy
@ fpmurphy1 Sag mehr.
Kusalananda
@ Kusalananda. Bash wurde noch nie als POSIX-Reklamation zertifiziert. Um "POSIX-Kompatibilität" in Bash zu erhalten, müssen Sie Bash mit der --posixBefehlszeilenoption aufrufen oder "set -o posix
fpmurphy
@ fpmurphy1 Ja, aber die Sortierung der Erweiterung der Dateinamen-Globbing-Zeichen wird vom Bash- posixModus nicht beeinflusst . Siehe gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html Dies lässt mich glauben (hoffentlich), dass die Sortierung POSIX-kompatibel ist.
Kusalananda
1

Wenn das primäre Ziel darin besteht, Eingabedateien nach ihrem Alter zu sortieren, das älteste zuerst, können Sie schreiben

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

Und wenn es sich auch um gedrehte und komprimierte Protokolle handelt:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
Sultane des Swing
quelle
4
Es wurde erwähnt, dass die Zeitstempel in den Dateien nicht vertrauenswürdig waren.
Kusalananda
3
@ Kusalananda, das ist richtig, unsere Systemzeit wird allgemein als Zufallsgenerator angesehen :)
Wossname