Ich habe eine Zeile (oder viele Zeilen) mit Zahlen, die durch ein beliebiges Zeichen begrenzt sind. Mit welchen UNIX-Tools kann ich die Elemente jeder Zeile numerisch sortieren, wobei das Trennzeichen beibehalten wird?
Beispiele beinhalten:
- Liste der Zahlen; Eingabe :
10 50 23 42
; sortiert:10 23 42 50
- IP Adresse; Eingabe :
10.1.200.42
; sortiert:1.10.42.200
- CSV; Eingabe :
1,100,330,42
; sortiert:1,42,100,330
- pfeifenbegrenzt; Eingabe :
400|500|404
; sortiert:400|404|500
Da das Trennzeichen beliebig ist, können Sie eine Antwort mit einem einstelligen Trennzeichen Ihrer Wahl bereitstellen (oder erweitern).
sort
numeric-data
Jeff Schaller
quelle
quelle
cut
der mit seiner-d
Option beliebige Trennzeichen unterstützt .4,325 comma 55 comma 42,430
nicht und auch nicht1.5 period 4.2
).Antworten:
Sie können dies erreichen mit:
Ersetzen Sie Punkte
.
durch Ihr Trennzeichen.Fügen Sie
-u
demsort
obigen Befehl hinzu, um die Duplikate zu entfernen.oder mit
gawk
( GNUawk
) können wir viele Zeilen verarbeiten, während das Obige auch erweitert werden kann:Ersetzen Sie
*
als FeldtrennzeichenSEP='*'
durch Ihr Trennzeichen .Hinweise:
Möglicherweise müssen Sie die
-g, --general-numeric-sort
Optionsort
anstelle von verwenden-n, --numeric-sort
, um eine beliebige Klasse von Zahlen (Ganzzahl, Gleitkomma, Wissenschaft, Hexadezimal usw.) zu verarbeiten.Wenn
awk
keine Änderung erforderlich ist, werden diese weiterhin behandelt.quelle
Mit
perl
gibt es eine offensichtliche Version; Teilen Sie die Daten auf, sortieren Sie sie und verbinden Sie sie erneut.Das Trennzeichen muss zweimal aufgeführt werden (einmal in
split
und einmal injoin
)zB für a
,
So
Da
split
es sich um eine Regex handelt, muss das Zeichen möglicherweise in Anführungszeichen gesetzt werden:Mit den Optionen
-a
und-F
können Sie die Aufteilung entfernen. Mit der-p
Schleife wie zuvor und setzen Sie die Ergebnisse auf$_
, die automatisch gedruckt werden:quelle
-l
Option anstelle von verwendenchomp
. Dadurch wird auch die neue Zeile beim Drucken wieder hinzugefügt. Siehe auch-a
(mit-F
) für den Teilung.-l
und-F
ist es noch schöner:perl -F'/\./' -le 'print join(".", sort {$a <=> $b} @F)'
-l
Option; Das hatte ich verpasst!-F
Flag ursprünglich nicht verwendet, weil es nicht in allen Versionen richtig funktioniert (z. B. Ihre Zeile in CentOS 7 - Perl 5.16.3 - gibt eine leere Ausgabe zurück, obwohl es unter Debian 9 einwandfrei funktioniert). In Kombination-p
ergibt sich jedoch ein etwas kleineres Ergebnis, weshalb ich dies als Alternative zur Antwort hinzufügte. zeigt, wie-F
verwendet werden kann. Vielen Dank!-a
und-n
Optionen, wann-F
und-n
wann-a
verwendet wird ... also wechseln Sie einfach-le
zu-lane
Mit Python und einer ähnlichen Idee wie in Stephen Harris 'Antwort :
Also so etwas wie:
Leider ist die manuelle Ausführung der E / A weitaus weniger elegant als bei der Perl-Version.
quelle
Bash-Skript:
Beispiel:
Beyogen auf
Teilen Sie die Zeichenfolge in Bash in ein Array auf
So sortieren Sie ein Array in Bash
Elemente eines Arrays verbinden?
quelle
Schale
Das Laden einer höheren Sprache braucht Zeit.
Für einige Zeilen kann die Shell selbst eine Lösung sein.
Wir können den externen Befehl
sort
und den Befehl verwendentr
. Einer ist sehr effizient beim Sortieren von Zeilen und der andere konvertiert effektiv ein Trennzeichen in Zeilenumbrüche:Dies muss nur wegen der Verwendung von Bash
<<<
. Wenn dies durch ein Here-Doc ersetzt wird, gilt die Lösung für posix.Dies ist in der Lage zu sortieren Felder mit Tabulatoren, Leerzeichen oder der Schale glob Zeichen (
*
,?
,[
). Keine Zeilenumbrüche, da jede Zeile sortiert wird.Wechseln Sie
<<<"$2"
zu<"$2"
, um Dateinamen zu verarbeiten, und nennen Sie es wie folgt:Das Trennzeichen ist für die gesamte Datei gleich. Wenn dies eine Einschränkung ist, könnte sie verbessert werden.
Die Verarbeitung einer Datei mit nur 6000 Zeilen dauert jedoch 15 Sekunden. Die Shell ist wirklich nicht das beste Werkzeug, um Dateien zu verarbeiten.
Awk
Für mehr als ein paar Zeilen (mehr als ein paar Zehner) ist es besser, eine echte Programmiersprache zu verwenden. Eine awk-Lösung könnte sein:
Dies dauert nur 0,2 Sekunden für dieselbe 6000-Zeilen-Datei, die oben erwähnt wurde.
Verstehen Sie, dass die
<"$2"
for-Dateien wieder in<<<"$2"
for-Zeilen innerhalb von Shell-Variablen geändert werden können.Perl
Die schnellste Lösung ist Perl.
Wenn Sie eine Datei ändern möchten, ändern Sie
<<<"$a"
sie einfach"$a"
und fügen Sie sie-i
zu Perl-Optionen hinzu, um die Datei-Edition "an Ort und Stelle" zu machen:quelle
Verwenden
sed
zum Sortieren von Oktetten einer IP-Adressesed
verfügt nicht über eine integrierte insort
Funktion, aber wenn Sie Ihre Daten ausreichend in Bereich (zB mit IP - Adressen) eingeschränkt ist, können Sie einen sed - Skript , dass manuell implementiert eine einfache erzeugen Blasensortierung . Der grundlegende Mechanismus besteht darin, nach benachbarten Nummern zu suchen, die nicht in der richtigen Reihenfolge sind. Wenn die Nummern nicht in Ordnung sind, tauschen Sie sie aus.Das
sed
Skript selbst enthält zwei Such- und Austauschbefehle für jedes Paar von Zahlen außerhalb der Reihenfolge: einen für die ersten beiden Oktettpaare (wodurch ein abschließendes Trennzeichen vorhanden sein muss, um das Ende des dritten Oktetts zu markieren) und a zweite für das dritte Oktettpaar (Ende mit EOL). Wenn Swaps auftreten, verzweigt das Programm an den Anfang des Skripts und sucht nach Zahlen, die nicht in der richtigen Reihenfolge sind. Andernfalls wird es beendet.Das generierte Skript ist teilweise:
Bei diesem Ansatz wird der Punkt als Trennzeichen fest codiert, das maskiert werden muss, da er sonst für die Syntax regulärer Ausdrücke "speziell" wäre (wobei jedes Zeichen zulässig ist).
Um ein solches sed-Skript zu generieren, führt diese Schleife Folgendes aus:
Leiten Sie beispielsweise die Ausgabe dieses Skripts in eine andere Datei um
sort-ips.sed
.Ein Probelauf könnte dann so aussehen:
Die folgende Variante der Erzeugungs Skript verwendet die Wortgrenzmarkierungen
\<
und\>
auf die Notwendigkeit des zweiten Wechsel loszuwerden. Dadurch wird auch die Größe des generierten Skripts von 1,3 MB auf knapp 900 KB reduziert und die Laufzeit des Skripts erheblich reduziertsed
(auf etwa 50% bis 75% des Originals, je nachdem, welchesed
Implementierung verwendet wird):quelle
sed
ist lächerlich, weshalb es eine interessante Herausforderung ist.Hier eine Bash, die den Begrenzer selbst errät:
Es ist vielleicht nicht sehr effizient oder sauber, aber es funktioniert.
Verwenden Sie wie
bash my_script.sh "00/00/18/29838/2"
.Gibt einen Fehler zurück, wenn dasselbe Trennzeichen nicht konsistent verwendet wird oder wenn zwei oder mehr Trennzeichen aufeinander folgen.
Wenn das verwendete Trennzeichen ein Sonderzeichen ist, wird es maskiert (andernfalls wird
sed
ein Fehler zurückgegeben).quelle
Diese Antwort basiert auf einem Missverständnis des Q., aber in einigen Fällen ist sie trotzdem richtig. Wenn es sich bei der Eingabe ausschließlich um natürliche Zahlen handelt und nur ein Trennzeichen pro Zeile vorhanden ist (wie bei den Beispieldaten im Q.), funktioniert dies ordnungsgemäß. Es werden auch Dateien mit Zeilen verarbeitet, die jeweils ein eigenes Trennzeichen haben. Dies ist etwas mehr als gewünscht.
Diese Shell-Funktion stammt
read
aus der Standardeingabe, verwendet die POSIX-Parametersubstitution , um das spezifische Trennzeichen in jeder Zeile zu finden (gespeichert in$d
), und ersetzt die Daten dieser Zeiletr
durch$d
eine neue Zeile\n
undsort
s und stellt dann die ursprünglichen Trennzeichen jeder Zeile wieder her:Auf die im OP angegebenen Daten angewendet :
Ausgabe:
quelle
Für beliebige Trennzeichen:
Bei einer Eingabe wie:
Es gibt:
quelle
Dies sollte alle nicht-stelligen (0-9) Trennzeichen behandeln. Beispiel:
Ausgabe:
quelle
Mit
perl
:Mit
ruby
, was etwas ähnlich ist wieperl
Benutzerdefinierter Befehl und Übergabe nur der Trennzeichenfolge (kein regulärer Ausdruck). Funktioniert, wenn die Eingabe auch schwebende Daten enthält
Benutzerdefinierter Befehl für
perl
Weiterführende Literatur - Ich hatte bereits diese praktische Liste von Perl / Ruby-Einzeilern
quelle
Das Folgende ist eine Variation von Jeffs Antwort in dem Sinne, dass sie a erzeugt
sed
Skript , das die Blasensortierung ausführt, aber ausreichend unterschiedlich ist, um eine eigene Antwort zu rechtfertigen.Der Unterschied besteht darin, dass anstelle von O (n ^ 2) grundlegenden regulären Ausdrücken O (n) erweiterte reguläre Ausdrücke generiert werden. Das resultierende Skript wird ungefähr 15 KB groß sein. Die Laufzeit des
sed
Skripts beträgt Sekundenbruchteile (das Generieren des Skripts dauert etwas länger).Es ist auf das Sortieren positiver Ganzzahlen beschränkt, die durch Punkte begrenzt sind, aber nicht auf die Größe der Ganzzahlen (nur erhöhen)
255
in der Hauptschleife) oder die Anzahl der Ganzzahlen beschränkt. Das Trennzeichen kann durch Änderndelim='.'
des Codes geändert werden .Es ist mir ein Rätsel, die regulären Ausdrücke richtig zu machen, also werde ich die Details für einen weiteren Tag beschreiben.
Das Skript sieht ungefähr so aus:
Die Idee hinter den generierten regulären Ausdrücken besteht darin, Musterübereinstimmungen für Zahlen vorzunehmen, die kleiner als jede Ganzzahl sind. Diese beiden Nummern wären nicht in der richtigen Reihenfolge und werden daher getauscht. Die regulären Ausdrücke sind in mehrere ODER-Optionen gruppiert. Achten Sie genau auf die Bereiche, die an jeden Artikel angehängt sind, manchmal auch. Dies
{0}
bedeutet, dass der unmittelbar vorherige Artikel bei der Suche weggelassen werden soll. Die Regex-Optionen von links nach rechts stimmen mit Zahlen überein, die kleiner als die angegebene Zahl sind, und zwar durch:Um ein Beispiel zu formulieren, nehmen Sie
101
(mit zusätzlichen Leerzeichen für die Lesbarkeit):Hier erlaubt der erste Wechsel die Zahlen 100 bis 100; Die zweite Abwechslung erlaubt 0 bis 99.
Ein weiteres Beispiel ist
154
:Hier erlaubt die erste Option 150 bis 153; Die zweite erlaubt 100 bis 149 und die letzte erlaubt 0 bis 99.
Viermal in einer Schleife testen:
Ausgabe:
quelle
Eingabe in mehrere Zeilen aufteilen
Mit
tr
können Sie die Eingabe mit einem beliebigen Trennzeichen in mehrere Zeilen aufteilen.Diese Eingabe kann dann durchlaufen werden
sort
(unter Verwendung,-n
wenn die Eingabe numerisch ist).Wenn Sie das Trennzeichen in der Ausgabe beibehalten möchten, können Sie es
tr
erneut verwenden, um das Trennzeichen wieder hinzuzufügen.zB Leerzeichen als Trennzeichen verwenden
cat input.txt | tr " " "\n" | sort -n | tr "\n" " "
Eingabe:
1 2 4 1 4 32 18 3
Ausgabe:1 1 2 3 4 4 18 32
quelle