GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Erzeugt der Bash Star * Wildcard immer eine (aufsteigend) sortierte Liste?

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.


Linux
  1. Anpassen der Bash-Shell

  2. Warum enthält die Bash-Übersetzungsdatei nicht alle Fehlertexte?

  3. Wie funktioniert der Continue-Befehl in einem Bash-Skript mit eingebetteten Schleifen?

  4. Wie kann man Bash Autocomplete anpassen, um die Dateien in einem anderen Verzeichnis aufzulisten?

  5. Warum funktioniert die Regex in Bash nur, wenn es sich um eine Variable handelt und nicht direkt?

Die Liste nützlicher Bash-Tastaturkürzel

Bash For Loop – Die praktischste Anleitung

Ist ~ immer gleich $home?

Die Bash‘?

Wie kann ich die Größe jeder Datei und jedes Verzeichnisses auflisten und in Bash nach absteigender Größe sortieren?

Hat das Root-Konto immer UID/GID 0?