Im Gegensatz zu ksh oder zsh bietet bash keine integrierte Unterstützung für das Sortieren von Arrays oder Listen beliebiger Zeichenfolgen. Es kann Globs oder die Ausgabe von alias
oder set
oder sortieren typeset
(obwohl die letzten 3 nicht in der Sortierreihenfolge des Benutzers enthalten sind), aber das kann hier praktisch nicht verwendet werden.
Es gibt nichts in der POSIX-Toolchest, das beliebige Listen von Zeichenfolgen leicht sortieren kann¹ ( sort
sortiert Zeilen, also nur kurze (LINE_MAX ist oft kürzer als PATH_MAX) Folgen von Zeichen außer NUL und Newline, während Dateipfade nicht leere Folgen von anderen Bytes sind als 0).
Während Sie also Ihren eigenen Sortieralgorithmus in awk
(mithilfe des <
Zeichenfolgenvergleichsoperators) oder sogarbash
(mithilfe [[ < ]]
) implementieren können , ist es für beliebige Pfade bash
möglicherweise am einfachsten, auf Folgendes zurückzugreifen perl
:
Mit bash4.4+
könnten Sie tun:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
Das gibt eine strcmp()
ähnliche Reihenfolge. Fügen Sie für eine Reihenfolge, die auf den Sortierregeln des Gebietsschemas wie in Globs oder der Ausgabe von basiert ls
, ein -Mlocale
Argument hinzu perl
. Für numerische Sortierung (mehr wie GNU sort -g
wie es unterstützt Zahlen wie +3
, 1.2e-5
und nicht die Tausendertrennzeichen , wenn auch nicht hexadimals), verwenden <=>
statt cmp
(und wieder -Mlocale
für das Dezimalzeichen des Benutzers wie für die geehrt werden sort
Befehl).
Sie sind durch die maximale Größe der Argumente für einen Befehl begrenzt. Um dies zu vermeiden, können Sie die Liste der Dateien perl
auf ihrem Standard anstatt über Argumente übergeben:
readarray -td '' sorted_filearray < <(
printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
Bei älteren Versionen von bash
können Sie eine while IFS= read -rd ''
Schleife anstelle von verwenden readarray -d ''
oder perl
die Liste der ordnungsgemäß zitierten Pfade ausgeben, an die Sie sie übergeben können eval "array=($(perl...))"
.
Mit zsh
können Sie eine Glob-Erweiterung vortäuschen, für die Sie eine Sortierreihenfolge definieren können:
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
Mit reply=($filearray)
erzwingen wir tatsächlich die Glob-Erweiterung (die anfangs nur war /
), um die Elemente des Arrays zu sein. Dann definieren wir die Sortierreihenfolge basierend auf dem Ende des Dateinamens.
strcmp()
Legen Sie für eine ähnliche Reihenfolge das Gebietsschema auf C fest. Fügen Sie für eine numerische Sortierung (ähnlich wie bei GNU sort -V
, sort -n
die beim Vergleich keinen signifikanten Unterschied macht, 1.4
und 1.23
(in Gebietsschemas, in denen .
die Dezimalstelle steht) beispielsweise) das n
Glob-Qualifikationsmerkmal hinzu.
Stattdessen oe{expression}
können Sie auch eine Funktion verwenden, um eine Sortierreihenfolge wie folgt zu definieren:
by_tail() REPLY=$REPLY:t
oder fortgeschrittenere wie:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
(also a/foo2bar3.pdf
(2,3 Zahlen) sortiert nach b/bar1foo3.pdf
(1,3) aber vor c/baz2zzz10.pdf
(2,10)) und verwendet als:
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
Natürlich können diese auf echte Globs angewendet werden, da sie in erster Linie dafür vorgesehen sind. Zum Beispiel für eine Liste von pdf
Dateien in einem beliebigen Verzeichnis, sortiert nach Basisname / Schwanz:
pdfs=(**/*.pdf(N.oe+by_tail))
¹ Wenn eine strcmp()
sortierte Sortierung akzeptabel ist und für kurze Zeichenfolgen, können Sie die Zeichenfolgen in ihre Hex-Codierung umwandeln, awk
bevor Sie sie übergeben sort
und nach dem Sortieren wieder umwandeln.