Ich habe eine CSV-Datei, die so aussieht
AS2345, ASDF1232, Mr. Plain Example, 110 Binary Ave., Atlantis, RI, 12345, (999) 123-5555, 1,56 AS2345, ASDF1232, Mrs. Plain Example, 1121110 Ternary st. 110 Binary Ave., Atlantis, RI, 12345, (999) 123-5555, 1,56 AS2345, ASDF1232, Mr. Plain Example, 110 Binary Ave., Liberty City, RI, 12345, (999) 123-5555, 1,56 AS2345, ASDF1232, Mr. Plain Example, 110 Ternary Ave., Some City, RI, 12345, (999) 123-5555, 1,56
Ich muss es nach Zeilenlänge einschließlich Leerzeichen sortieren. Der folgende Befehl enthält keine Leerzeichen. Gibt es eine Möglichkeit, ihn so zu ändern, dass er für mich funktioniert?
cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'
Antworten:
Antworten
Oder um Ihre ursprüngliche (möglicherweise unbeabsichtigte) Untersortierung von Zeilen gleicher Länge durchzuführen:
In beiden Fällen haben wir Ihr angegebenes Problem gelöst, indem wir uns für Ihren endgültigen Schnitt von awk entfernt haben.
Linien gleicher Länge - was bei Krawatte zu tun ist:
In der Frage wurde nicht angegeben, ob eine weitere Sortierung für Zeilen mit übereinstimmender Länge gewünscht wurde oder nicht. Ich habe angenommen, dass dies unerwünscht ist, und die Verwendung von
-s
(--stable
) vorgeschlagen, um zu verhindern, dass solche Zeilen gegeneinander sortiert werden, und sie in der relativen Reihenfolge zu halten, in der sie in der Eingabe vorkommen.(Diejenigen, die mehr Kontrolle über das Sortieren dieser Bindungen wünschen, könnten sich die
--key
Option der Sortierung ansehen .)Warum der Lösungsversuch der Frage fehlschlägt (Aufbau einer awk-Leitung):
Es ist interessant, den Unterschied zwischen:
Sie ergeben jeweils
In dem entsprechenden Abschnitt des Handbuchs von (gawk) wird nur erwähnt, dass awk die gesamten $ 0 (basierend auf dem Trennzeichen usw.) neu erstellt, wenn Sie ein Feld ändern. Ich denke, es ist kein verrücktes Verhalten. Es hat dies:
"Schließlich gibt es Zeiten, in denen es zweckmäßig ist, awk zu zwingen, den gesamten Datensatz unter Verwendung des aktuellen Werts der Felder und des OFS neu zu erstellen. Verwenden Sie dazu die scheinbar harmlose Zuweisung:"
"Dies zwingt awk, den Datensatz neu zu erstellen."
Testeingabe einschließlich einiger Zeilen gleicher Länge:
quelle
cat $@
das kaputt ist. Sie wollen es auf jeden Fall zitieren, wiecat "$@"
Die AWK-Lösung von neillb ist großartig, wenn Sie sie wirklich verwenden möchten,
awk
und sie erklärt, warum es dort problematisch ist. Wenn Sie jedoch möchten, dass die Arbeit schnell erledigt wird und es Ihnen egal ist, in was Sie sie ausführen, ist eine Lösung die Verwendung Perlssort()
Funktion mit einer benutzerdefinierten Caparison-Routine zum Durchlaufen der Eingabezeilen. Hier ist ein Einzeiler:Sie können dies in Ihre Pipeline einfügen, wo immer Sie es benötigen, indem Sie entweder STDIN (von
cat
oder eine Shell-Umleitung) empfangen oder einfach den Dateinamen perl als weiteres Argument angeben und die Datei öffnen lassen.In meinem Fall brauchte ich zuerst die längsten Zeilen, also habe ich sie ausgetauscht
$a
und$b
verglichen.quelle
cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
Versuchen Sie stattdessen diesen Befehl:
quelle
Benchmark-Ergebnisse
Nachfolgend finden Sie die Ergebnisse eines Benchmarks für Lösungen aus anderen Antworten auf diese Frage.
Testmethode
Ergebnisse
perl
Lösung dauerte 11,2 Sekundenperl
Lösung dauerte 11,6 Sekundenawk
lösung Nr. 1 dauerte 20 sekundenawk
lösung Nr. 2 dauerte 23 sekundenawk
Lösung von Anubhava dauerte 24 Sekundenawk
Lösung dauerte 25 Sekundenbash
Lösung nimmt 400x länger als dieawk
Lösungen (mit Hilfe eines abgestumpften Testfall von 100000 Linien). Es funktioniert gut, dauert nur ewig.Zusätzliche
perl
OptionAußerdem habe ich eine weitere Perl-Lösung hinzugefügt:
quelle
Pure Bash:
quelle
Die
length()
Funktion enthält Leerzeichen. Ich würde nur geringfügige Anpassungen an Ihrer Pipeline vornehmen (einschließlich der Vermeidung von UUOC ).Der
sed
Befehl entfernt direkt die vomawk
Befehl hinzugefügten Ziffern und Doppelpunkte . Alternativ können Sie Ihre Formatierung beibehalten vonawk
:quelle
Ich habe festgestellt, dass diese Lösungen nicht funktionieren, wenn Ihre Datei Zeilen enthält, die mit einer Zahl beginnen, da sie zusammen mit allen gezählten Zeilen numerisch sortiert werden. Die Lösung ist , zu geben ,
sort
die-g
(allgemein numerische-sort) flag anstelle von-n
(numeric-sort):quelle
-n
auf Ihren Vorschlag gefunden-g
, um eine Verbesserung zu erzielen, daher erwarte ich dies nicht. In meiner Antwort habe ich mich nun damit befasst, wie das Untersortieren von Zeilen gleicher Länge (mit--stable
) verboten werden kann . Ob Sie das gemeint haben oder nicht, danke, dass Sie mich darauf aufmerksam gemacht haben! Ich habe auch eine überlegte Eingabe zum Testen hinzugefügt.awk
Teil generiert eine Liste von Zeilen, denen Zeilenlänge und ein Leerzeichen vorangestellt sind. Das Weiterleitensort -n
funktioniert wie erwartet. Wenn jedoch eine dieser Zeilen am Anfang bereits eine Nummer hat, beginnen diese Zeilen mit Länge + Leerzeichen + Nummer.sort -n
ignoriert diesen Raum und behandelt ihn als eine aus Länge + Zahl verkettete Zahl. Die Verwendung des-g
Flags stoppt stattdessen beim ersten Leerzeichen und ergibt eine korrekte Sortierung. Probieren Sie es selbst aus, indem Sie eine Datei mit Zeilen mit Präfix erstellen und den Befehl Schritt für Schritt ausführen.sort -n
der Raum ignoriert und eine falsche Sortierung erzeugt.sort -g
gibt die richtige Reihenfolge aus.-n
in nicht reproduzierensort (GNU coreutils) 8.21
. In derinfo
Dokumentation wird beschrieben-g
, dass diese weniger effizient und möglicherweise weniger präzise ist (sie konvertiert Zahlen in Floats). Verwenden Sie sie daher wahrscheinlich nicht, wenn Sie dies nicht benötigen.-n
: "Numerisch sortieren. Die Zahl beginnt in jeder Zeile und besteht aus optionalen Leerzeichen, einem optionalen '-' Zeichen und null oder mehr Ziffern, die möglicherweise durch Tausende Trennzeichen getrennt sind, optional gefolgt von einem Dezimalzeichen und null oder mehr Ziffern Eine leere Zahl wird als '0' behandelt. Das Gebietsschema 'LC_NUMERIC' gibt das Dezimalzeichen und das Tausendertrennzeichen an. Standardmäßig ist ein Leerzeichen ein Leerzeichen oder eine Registerkarte, das Gebietsschema 'LC_CTYPE' kann dies jedoch ändern. "Mit POSIX Awk:
Beispiel
quelle
1) reine awk-Lösung. Nehmen wir an, dass die Zeilenlänge dann nicht mehr als 1024 sein kann
Katzendateiname | awk 'BEGIN {min = 1024; s = "";} {l = Länge ($ 0); wenn (l <min) {min = l; s = $ 0;}} END {print s} '
2) Eine Liner-Bash-Lösung unter der Annahme, dass alle Zeilen nur 1 Wort haben, kann jedoch für jeden Fall überarbeitet werden, in dem alle Zeilen die gleiche Anzahl von Wörtern haben:
LINES = $ (Dateiname der Katze); für k in $ LINES; printf "$ k"; echo $ k | wc -L; erledigt | sort -k2 | Kopf -n 1 | schneide -d "" -f1
quelle
Hier ist eine Multibyte-kompatible Methode zum Sortieren von Zeilen nach Länge. Es benötigt:
wc -m
steht Ihnen zur Verfügung (macOS hat es).LC_ALL=UTF-8
. B. durch Festlegen . Sie können dies entweder in Ihrem .bash_profile oder einfach durch Voranstellen vor dem folgenden Befehl festlegen.testfile
hat eine Zeichencodierung, die Ihrem Gebietsschema entspricht (z. B. UTF-8).Hier ist der vollständige Befehl:
Teil für Teil erklären:
l=$0; gsub(/\047/, "\047\"\047\"\047", l);
← erstellt eine Kopie jeder Zeile in einer awk-Variablenl
und maskiert jede Zeile doppelt,'
damit die Zeile sicher als Shell-Befehl wiedergegeben werden kann (\047
ist ein einfaches Anführungszeichen in Oktalschreibweise).cmd=sprintf("echo \047%s\047 | wc -m", l);
← Dies ist der Befehl, den wir ausführen, der die maskierte Zeile wiedergibtwc -m
.cmd | getline c;
← führt den Befehl aus und kopiert den Zeichenanzahlwert, der in die Variable awk zurückgegeben wirdc
.close(cmd);
← Schließen Sie die Pipe zum Shell-Befehl, um zu vermeiden, dass die Anzahl der geöffneten Dateien in einem Prozess auf ein System begrenzt wird.sub(/ */, "", c);
← schneidet Leerzeichen vom Zeichenanzahlwert ab, der von zurückgegeben wirdwc
.{ print c, $0 }
← druckt den Zeichenanzahlwert der Zeile, ein Leerzeichen und die ursprüngliche Zeile.| sort -ns
← sortiert die Zeilen (nach vorangestellten Zeichenanzahlwerten) numerisch (-n
) und behält eine stabile Sortierreihenfolge bei (-s
).| cut -d" " -f2-
← entfernt die vorangestellten Zeichenanzahlwerte.Es ist langsam (nur 160 Zeilen pro Sekunde auf einem schnellen Macbook Pro), da für jede Zeile ein Unterbefehl ausgeführt werden muss.
Alternativ können Sie dies nur mit
gawk
(ab Version 3.1.5 ist gawk Multibyte-fähig) ausführen, was erheblich schneller wäre. Es ist sehr mühsam, alle Zeilenumbrüche und doppelten Anführungszeichen auszuführen, um die Zeilen sicher durch einen Shell-Befehl von awk zu leiten. Dies ist jedoch die einzige Methode, bei der keine zusätzliche Software installiert werden muss (gawk ist standardmäßig nicht verfügbar) Mac OS).quelle