Sie brauchen nicht wirklich so viel Code:
IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS
Unterstützt Leerzeichen in Elementen (solange es sich nicht um eine neue Zeile handelt) und funktioniert in Bash 3.x.
z.B:
$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]
Hinweis: @sorontar hat darauf hingewiesen, dass Vorsicht geboten ist, wenn Elemente Platzhalter wie *
oder enthalten ?
:
Der sortierte = ($ (...)) Teil verwendet den Operator "split and glob". Sie sollten glob deaktivieren: set -f
oder set -o noglob
oder shopt -op noglob
oder ein Element des Arrays wie *
wird zu einer Liste von Dateien erweitert.
Was ist los:
Das Ergebnis ist ein Höhepunkt von sechs Dingen, die in dieser Reihenfolge geschehen:
IFS=$'\n'
"${array[*]}"
<<<
sort
sorted=($(...))
unset IFS
Zuerst die IFS=$'\n'
Dies ist ein wichtiger Teil unserer Operation, der das Ergebnis von 2 und 5 folgendermaßen beeinflusst:
Gegeben:
"${array[*]}"
Erweitert sich auf jedes Element, das durch das erste Zeichen von begrenzt ist IFS
sorted=()
Erstellt Elemente durch Aufteilen auf jedes Zeichen von IFS
IFS=$'\n'
Richtet die Dinge so ein, dass Elemente mit einer neuen Zeile als Trennzeichen erweitert und später so erstellt werden, dass jede Zeile zu einem Element wird. (dh Aufteilen in eine neue Zeile.)
Das Abgrenzen durch eine neue Zeile ist wichtig, da dies so sort
funktioniert (Sortieren pro Zeile). Das Teilen durch nur eine neue Zeile ist nicht so wichtig, muss jedoch Elemente beibehalten, die Leerzeichen oder Tabulatoren enthalten.
Der Standardwert von IFS
ist ein Leerzeichen , eine Registerkarte , gefolgt von einer neuen Zeile , und wäre für unseren Vorgang nicht geeignet.
Als nächstes das sort <<<"${array[*]}"
Teil
<<<
, hier Strings genannt , nimmt die Erweiterung von "${array[*]}"
, wie oben erläutert, und speist sie in die Standardeingabe von ein sort
.
In unserem Beispiel sort
wird folgende Zeichenfolge eingegeben:
a c
b
f
3 5
Da sort
sortiert , produziert es:
3 5
a c
b
f
Als nächstes das sorted=($(...))
Teil
Der $(...)
Teil, der als Befehlssubstitution bezeichnet wird , bewirkt, dass content ( sort <<<"${array[*]}
) wie ein normaler Befehl ausgeführt wird, während die resultierende Standardausgabe als Literal verwendet wird, das dorthin geht, wo immer es $(...)
war.
In unserem Beispiel ergibt dies etwas Ähnliches wie das einfache Schreiben:
sorted=(3 5
a c
b
f
)
sorted
wird dann zu einem Array, das durch Aufteilen dieses Literals in jede neue Zeile erstellt wird.
Endlich, das unset IFS
Dies setzt den Wert von IFS
auf den Standardwert zurück und ist nur eine gute Vorgehensweise.
Es soll sicherstellen, dass wir keine Probleme mit irgendetwas verursachen, auf das wir uns IFS
später in unserem Skript stützen. (Andernfalls müssten wir uns daran erinnern, dass wir die Dinge umgestellt haben - etwas, das für komplexe Skripte möglicherweise unpraktisch ist.)
IFS
, es wird Ihre Elemente in kleine Teile teilen, wenn sie Leerzeichen enthalten. Versuchen Sie das zB mitIFS=$'\n'
weggelassen und sehen Sie!IFS
teilt es Ihre Elemente in kleine Teile auf, wenn sie nur eine bestimmte Art von Leerzeichen enthalten. Gut; nicht perfekt :-)unset IFS
notwendig? Ich dachte, das VoranstellenIFS=
eines Befehls umfasste nur die Änderung dieses Befehls und kehrte danach automatisch zum vorherigen Wert zurück.sorted=()
es sich nicht um einen Befehl handelt, sondern um eine zweite Variablenzuweisung.Ursprüngliche Antwort:
Ausgabe:
Beachten Sie, dass diese Version mit Werten umgeht , die Sonderzeichen oder Leerzeichen enthalten ( außer Zeilenumbrüche).
Hinweis: Readarray wird in Bash 4+ unterstützt.
Bearbeiten Basierend auf dem Vorschlag von @Dimitre hatte ich es aktualisiert auf:
Dies hat den Vorteil, dass Sortierelemente mit korrekt eingebetteten Zeilenumbruchszeichen sogar verstanden werden. Leider, wie korrekt signalisiert @ruakh bedeute dies nicht , das das Ergebnis
readarray
wäre richtig , weilreadarray
keine andere Wahl hat zu verwenden , dieNUL
anstelle der normalen Zeilenumbrüche als Line-Separatoren.quelle
readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
sort -z
ist eine nützliche Verbesserung. Ich nehme an, die-z
Option ist eine GNU-Sortiererweiterung.sorted=(); while read -d $'\0' elem; do sorted[${#sorted[@]}]=$elem; done < <(printf '%s\0' "${array[@]}" | sort -z)
. Dies funktioniert auch, wenn Sie bash v3 anstelle von bash v4 verwenden, da Readarray in bash v3 nicht verfügbar ist.<
) kombiniert mit Prozessersetzung<(...)
. Oder intuitiv ausgedrückt: weil(printf "bla")
es keine Datei ist.Hier ist eine reine Bash-Quicksort-Implementierung:
Verwenden Sie als z.
Diese Implementierung ist rekursiv. Hier ist eine iterative Quicksortierung:
In beiden Fällen können Sie die Reihenfolge ändern, die Sie verwenden: Ich habe Zeichenfolgenvergleiche verwendet, aber Sie können arithmetische Vergleiche verwenden, die Änderungszeit der WRT-Datei vergleichen usw. Verwenden Sie einfach den entsprechenden Test. Sie können es sogar allgemeiner gestalten und ein erstes Argument verwenden lassen, das die Testfunktion verwendet, z.
Dann können Sie diese Vergleichsfunktion haben:
und verwenden:
Damit die Dateien im aktuellen Ordner nach Änderungszeit sortiert werden (neueste zuerst).
HINWEIS. Diese Funktionen sind reine Bash! Keine externen Dienstprogramme und keine Subshells! Sie sind sicher für alle lustigen Symbole, die Sie möglicherweise haben (Leerzeichen, Zeilenumbruchzeichen, Glob-Zeichen usw.).
quelle
sort
ausreicht, ist einesort
+read -a
-Lösung ab etwa 20 Elementen schneller und umso schneller, je mehr Elemente Sie bearbeiten. Beispiel: Auf meinem iMac Ende 2012 wird OSX 10.11.1 mit einem Fusion Drive ausgeführt: Array mit 100 Elementen: Ca. 0,03 s sek. (qsort()
) vs. 0,005 Sekunden. (sort
+read -a
); 1000-Elemente-Array: Ca. 0,375 Sekunden (qsort()
) vs. 0,014 Sekunden (sort
+read -a
).if [ "$i" -lt "$pivot" ]; then
war andernfalls die aufgelöste "2" <"10" true zurückgegeben. Ich glaube, das ist POSIX vs. Lexicographical; oder vielleicht Inline Link .Wenn Sie keine speziellen Shell-Zeichen in den Array-Elementen verarbeiten müssen:
Mit bash benötigen Sie ohnehin ein externes Sortierprogramm.
Mit zsh werden keine externen Programme benötigt und spezielle Shell-Zeichen sind einfach zu handhaben:
ksh muss ASCIIbetisch
set -s
sortieren .quelle
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@"
Und natürlich setzt der Befehl set die aktuellen Positionsparameter zurück, falls vorhanden.tl; dr :
Sortieren Sie das Array
a_in
und speichern Sie das Ergebnis ina_out
(Elemente dürfen keine eingebetteten Zeilenumbrüche enthalten [1] ):Bash v4 +:
Bash v3:
Vorteile gegenüber der Lösung von Antak :
Sie müssen sich keine Gedanken über versehentliches Globbing machen (versehentliche Interpretation der Array-Elemente als Dateinamenmuster), sodass kein zusätzlicher Befehl erforderlich ist, um das Globbing zu deaktivieren (
set -f
undset +f
es später wiederherzustellen).Sie müssen sich keine Gedanken über das Zurücksetzen
IFS
mit machenunset IFS
. [2]Optionales Lesen: Erklärung und Beispielcode
Der oben Mähdrescher Bash Code mit externem Dienstprogramm
sort
für eine Lösung , die Arbeiten mit beliebigen einzelnen -Linie Elementen und entweder lexikalischer oder numerischer Sortier (gegebenenfalls durch Feld) :Leistung : Bei etwa 20 Elementen oder mehr ist dies schneller als bei einer reinen Bash-Lösung - deutlich und zunehmend, sobald Sie mehr als 100 Elemente erreicht haben.
(Die genauen Schwellenwerte hängen von Ihrer spezifischen Eingabe, Maschine und Plattform ab.)
printf '%s\n' "${a_in[@]}" | sort
führt die Sortierung durch (standardmäßig lexikalisch - siehesort
POSIX-Spezifikation ):"${a_in[@]}"
Erweitert sich sicher auf die Elemente des Arraysa_in
als einzelne Argumente , unabhängig davon, was sie enthalten (einschließlich Leerzeichen).printf '%s\n'
druckt dann jedes Argument - dh jedes Array-Element - unverändert in einer eigenen Zeile.Beachten Sie die Verwendung einer Prozesssubstitution (
<(...)
) , um die sortierte Ausgabe als Eingabe fürread
/readarray
(über die Umleitung zu stdin ) bereitzustellen<
, daread
/readarray
in der aktuellen Shell ausgeführt werden muss (darf nicht in einer Subshell ausgeführt werden ), damit die Ausgabevariablea_out
sichtbar ist auf die aktuelle Shell (damit die Variable im Rest des Skripts definiert bleibt).Lesen
sort
der Ausgabe in eine Array-Variable :Bash v4 +:
readarray -t a_out
Liest die einzelnen Zeilen aus, die vonsort
in die Elemente der Array-Variablen ausgegeben werdena_out
, ohne die Nachfolge\n
in jedes Element (-t
) aufzunehmen.Bash v3:
readarray
existiert nicht,read
muss also verwendet werden:IFS=$'\n' read -d '' -r -a a_out
weistread
an, in die-a
Variable array ( ) zu lesena_out
, die gesamte Eingabe über Zeilen (-d ''
) zu lesen , sie jedoch durch Zeilenumbrüche (IFS=$'\n'
. ) In Array-Elemente aufzuteilen$'\n'
, wodurch eine wörtliche Zeilenumbruch (LF) erzeugt wird ) ist eine sogenannte ANSI C-Zeichenfolge ).(
-r
Eine Option, die praktisch immer verwendet werden sollteread
, deaktiviert die unerwartete Behandlung von\
Zeichen.)Kommentierter Beispielcode:
Aufgrund der Verwendung von
sort
ohne Optionen ergibt sich eine lexikalische Sortierung (Ziffernsortierung vor Buchstaben und Ziffernfolgen werden lexikalisch und nicht als Zahlen behandelt):Wenn Sie eine numerische Sortierung nach dem 1. Feld wünschen, verwenden Sie
sort -k1,1n
anstelle von nursort
, was ergibt (Nicht-Zahlen werden vor Zahlen sortiert und Zahlen werden korrekt sortiert):[1] Verwenden Sie zum Behandeln von Elementen mit eingebetteten Zeilenumbrüchen die folgende Variante (Bash v4 + mit GNU
sort
) :readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z)
.Die hilfreiche Antwort von Michał Górny enthält eine Bash v3-Lösung.
[2] Während
IFS
wird in der Schlag - v3 Variante gesetzt, wird die Änderung auf den Befehl scoped .Im Gegensatz dazu folgt
IFS=$'\n'
in Antaks Antwort eher eine Zuweisung als ein Befehl. In diesem Fall ist dieIFS
Änderung global .quelle
Bei der dreistündigen Zugfahrt von München nach Frankfurt (die ich nur schwer erreichen konnte, weil das Oktoberfest morgen beginnt) dachte ich an meinen ersten Beitrag. Die Verwendung eines globalen Arrays ist eine viel bessere Idee für eine allgemeine Sortierfunktion. Die folgende Funktion verarbeitet beliebige Zeichenfolgen (Zeilenumbrüche, Leerzeichen usw.):
Dies druckt:
Die gleiche Ausgabe wird aus erstellt
Beachten Sie, dass wahrscheinlich Bash intern Smart-Pointer verwendet, so dass der Swap-Betrieb könnte billig sein (obwohl ich bezweifle es). Zeigt jedoch,
bubble_sort
dass erweiterte Funktionen wiemerge_sort
auch in der Reichweite der Shell-Sprache liegen.quelle
local -n BSORT="$1"
zu Beginn der Funktion. Dann können Sie laufenbubble_sort myarray
, um Myarray zu sortieren .Eine weitere Lösung , die externe verwendet
sort
und meistert mit allen Sonderzeichen (außer NULs :)). Sollte mit bash-3.2 und GNU oder BSDsort
funktionieren (leider enthält POSIX nicht-z
).Schauen Sie sich zuerst die Eingabeumleitung am Ende an. Wir verwenden die
printf
eingebauten Array-Elemente mit Nullterminierung. Durch das Anführungszeichen wird sichergestellt, dass Array-Elemente unverändert übergeben werden. Durch die Besonderheiten der Shell wirdprintf
der letzte Teil der Formatzeichenfolge für jeden verbleibenden Parameter wiederverwendet. Das heißt, es entspricht so etwas wie:Die nullterminierte Elementliste wird dann an übergeben
sort
. Die-z
Option bewirkt, dass nullterminierte Elemente gelesen, sortiert und auch nullterminiert ausgegeben werden. Wenn Sie nur die eindeutigen Elemente benötigen, können Sie übergeben,-u
da es portabler ist alsuniq -z
. DasLC_ALL=C
sorgt für eine stabile Sortierreihenfolge unabhängig von locale - manchmal nützlich für Skripte. Wenn Sie möchten, dass dassort
Gebietsschema respektiert wird, entfernen Sie das.Das
<()
Konstrukt erhält den zu lesenden Deskriptor aus der erzeugten Pipeline und<
leitet die Standardeingabe derwhile
Schleife an diese weiter. Wenn Sie auf die Standardeingabe in der Pipe zugreifen müssen, können Sie eine andere Deskriptorübung für den Leser verwenden :).Nun zurück zum Anfang. Das
read
eingebaute liest die Ausgabe vom umgeleiteten Standard. Wenn Sie leer setzen, wird dieIFS
Wortaufteilung deaktiviert, was hier nichtread
erforderlich ist. Infolgedessen wird die gesamte Eingabezeile in die einzelne bereitgestellte Variable gelesen.-r
Option deaktiviert die Escape-Verarbeitung, die auch hier unerwünscht ist. Setzt schließlich-d ''
den Zeilenbegrenzer auf NUL - das heißt, es wirdread
angewiesen, Zeichenfolgen mit Nulltermin zu lesen.Infolgedessen wird die Schleife einmal für jedes aufeinanderfolgende nullterminierte Array-Element ausgeführt, wobei der Wert in gespeichert wird
e
. Das Beispiel fügt die Elemente nur in ein anderes Array ein, aber Sie können es vorziehen, sie direkt zu verarbeiten :).Dies ist natürlich nur eine der vielen Möglichkeiten, dasselbe Ziel zu erreichen. Aus meiner Sicht ist es einfacher als die Implementierung eines vollständigen Sortieralgorithmus in Bash und in einigen Fällen schneller. Es behandelt alle Sonderzeichen einschließlich Zeilenumbrüchen und sollte auf den meisten gängigen Systemen funktionieren. Am wichtigsten ist, dass es Ihnen etwas Neues und Fantastisches über Bash beibringen kann :).
quelle
e
Verwenden Sie die Variable REPLY, anstatt eine lokale Variable einzuführen und ein leeres IFS festzulegen.Versuche dies:
Ausgabe wird sein:
Problem gelöst.
quelle
Wenn Sie für jedes Element im Array eine eindeutige Ganzzahl berechnen können, gehen Sie wie folgt vor:
Dann können Sie diese Ganzzahlen als Array-Indizes verwenden, da Bash immer ein Array mit geringer Dichte verwendet, sodass Sie sich keine Gedanken über nicht verwendete Indizes machen müssen:
quelle
min sort:
quelle
Der Echo-Inhalt von new_array lautet:
quelle
Es gibt eine Problemumgehung für das übliche Problem von Leerzeichen und Zeilenumbrüchen:
Verwenden , um ein Zeichen , das nicht in der ursprünglichen Anordnung ist (wie
$'\1'
oder$'\4'
oder ähnliches).Diese Funktion erledigt den Job:
Dadurch wird das Array sortiert:
Dadurch wird beanstandet, dass das Quellarray das Problemumgehungszeichen enthält:
Beschreibung
wa
(Workaround-Zeichen) und ein Null-IFS$*
.[[ $* =~ [$wa] ]]
.exit 1
set -f
IFS=$'\n'
), eine Schleifenvariablex
und eine Newline var (nl=$'\n'
).$@
)."${@//$nl/$wa}"
.sort -n
.set --
.for x
sorted+=(…)
"${x//$wa/$nl}"
.quelle
Diese Frage sieht eng verwandt aus. Übrigens, hier ist ein Mergesort in Bash (ohne externe Prozesse):
quelle
Ich bin nicht davon überzeugt, dass Sie in Bash ein externes Sortierprogramm benötigen.
Hier ist meine Implementierung für den einfachen Blasensortierungsalgorithmus.
Dies soll drucken:
quelle
O(n^2)
. Ich erinnere mich an die meisten Sortieralgorithmen, dieO(n lg(n))
bis zum letzten Dutzend Elemente oder so verwenden. Für die letzten Elemente wird die Auswahlsortierung verwendet.quelle
sorted=($(echo ${array[@]} | tr " " "\n" | sort))
Im Geiste von Bash / Linux würde ich für jeden Schritt das beste Befehlszeilen-Tool verwenden.
sort
erledigt die Hauptaufgabe, benötigt jedoch eine durch Zeilenumbruch getrennte Eingabe anstelle von Leerzeichen, sodass die oben beschriebene sehr einfache Pipeline einfach Folgendes tut:Echo-Array-Inhalt -> Leerzeichen durch Zeilenumbruch ersetzen -> sortieren
$()
ist das Ergebnis zu wiederholen($())
ist das "Echo-Ergebnis" in ein Array zu setzenHinweis : Wie @sorontar in einem Kommentar zu einer anderen Frage erwähnt hat:
quelle
mapfile -t sorted < <(printf '%s\n' "${array[@]}" | sort)
sonstsorted=(); while IFS= read -r line; do sorted+=( "$line" ); done < <(printf '%s\n' | sort)
.echo ${array[@]} | tr " " "\n"
Dies wird unterbrochen, wenn die Felder des Arrays Leerzeichen und Glob-Zeichen enthalten. Außerdem erzeugt es eine Unterschale und verwendet einen nutzlosen externen Befehl. Und aufgrundecho
ist stumm, wird es brechen , wenn Ihr Array mit beginnt-e
,-E
oder-n
. Verwenden Sie stattdessen :printf '%s\n' "${array[@]}"
. Das andere Antimuster ist:($())
Das "Echo-Ergebnis" wird in ein Array eingefügt . Sicherlich nicht! Dies ist ein schreckliches Antimuster, das aufgrund der Pfadnamenerweiterung (Globbing) und der Wortteilung zerbricht. Benutze niemals diesen Horror.