Ich habe ein Verzeichnis voller Dateien mit Namen wie logXX
wobei XX eine aus zwei Zeichen bestehende, mit Nullen aufgefüllte Hexadezimalzahl in Großbuchstaben ist, wie zum Beispiel:
log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...
Im Allgemeinen gibt es insgesamt weniger als sagen wir 20 oder 30 Dateien. Auf das Datum und die Uhrzeit auf meinem speziellen System kann man sich nicht verlassen (ein eingebettetes System ohne zuverlässige NTP- oder GPS-Zeitquellen). Die Dateinamen werden jedoch wie oben gezeigt zuverlässig erhöht.
Ich möchte grep
Ich hatte gehofft, durch alle Dateien für den letzten einzelnen Protokolleintrag eines bestimmten Typs cat
zu gelangen die Dateien zusammen wie …
cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1
Allerdings ist mir aufgefallen, dass verschiedene Versionen von bash
oder sh
oder zsh
usw. haben möglicherweise unterschiedliche Vorstellungen darüber, wie der *
wird erweitert.
Der man bash
Seite sagt nicht, ob die Erweiterung von *
wäre eine eindeutig aufsteigende alphabetische Liste übereinstimmender Dateinamen. 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 auf cat /tmp/logs/log*
verlassen alle meine Protokolldateien in alphabetischer Reihenfolge verketten?
Akzeptierte Antwort:
In allen Shells werden Globs standardmäßig sortiert. Sie waren bereits unter /etc/glob
Helfer, der von Ken Thompsons Shell aufgerufen wurde, um Globs in der ersten Version von Unix in den frühen 70er Jahren zu erweitern (und der Globs ihren Namen gab).
Für sh
, POSIX erfordert jedoch, dass sie mittels strcoll()
sortiert werden , d. h. die Sortierreihenfolge im Gebietsschema des Benutzers verwendet, wie für ls
obwohl einige es immer noch über strcmp()
tun , die nur auf Bytewerten 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 können oben bemerken, dass für die Shells, die basierend auf dem Gebietsschema sortieren, hier auf einem GNU-System mit einem en_GB.UTF-8
Gebietsschema, der -
in den Dateinamen wird beim Sortieren ignoriert (die meisten Satzzeichen würden). Der ó
wird eher erwartet sortiert (zumindest für Briten) und die Groß-/Kleinschreibung wird ignoriert (außer wenn es darum geht, Unentschieden zu entscheiden).
Sie werden jedoch einige Inkonsistenzen für log① log② feststellen. Das liegt daran, dass die Sortierreihenfolge von ① und ② in GNU-Locales nicht definiert ist (derzeit; wird hoffentlich eines Tages behoben). Sie sortieren gleich, sodass Sie zufällige Ergebnisse erhalten.
Verwandte:Nachkommen verarbeiten?
Das Ändern des Gebietsschemas wirkt sich auf die Sortierreihenfolge aus. Sie können das Gebietsschema auf C setzen, um ein strcmp()
zu erhalten -ähnliche Sortierung:
$ 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 selbst bei reinen ASCII-Alnum-Zeichenfolgen zu Verwirrung führen können. Wie die tschechischen (zumindest auf GNU-Systemen), wo ch
ist ein Vergleichselement das sortiert nach h
:
$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch
Oder, wie von @ninjalj hervorgehoben, sogar noch seltsamere in ungarischen Gebietsschemata:
$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy
In zsh
, können Sie die Sortierung mit Glob-Qualifizierern 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 Art von echo *(n)
kann auch global mit dem numericglobsort
aktiviert werden Möglichkeit:
$ 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) von dieser Reihenfolge in diesem bestimmten Fall verwirrt sind (hier unter Verwendung meines britischen Gebietsschemas), finden Sie hier weitere Details.