Sortieren mit sed oder awk

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
medias
Beiträge: 90
Registriert: 18.05.2014 11:21:43

Sortieren mit sed oder awk

Beitrag von medias » 21.01.2015 23:20:35

Wie sortiere ich eine plain-text Datei mit einem Einzeiler nach z. B. dem zweit-letzten oder siebt-letzten "Wort" bzw. string eines Satzes und richte das Ergebnis nicht links- sondern rechtsbündig aus?

Das zweit- oder siebt-letzte Wort besteht aus Buchstaben und Zahlen (a-z, 0-9), aber keine Umlaute und nur Kleinbuchstaben. Es sind keine Spalten wie in einem Server logfile. Ich dachte ich könnte das mit awk lösen in dem ich die Einzelwörter als Spalten wie in einem logfile sehe, aber das funktioniert nicht. Ich habe auch versucht die Leerzeichen zwischen den Wörtern mit einem Punkt zu ersetzen und dann zu sortieren wie eine URL sortiert werden kann, aber das funktioniert nicht.

Benutzeravatar
hikaru
Moderator
Beiträge: 13593
Registriert: 09.04.2008 12:48:59

Re: Sortieren mit sed oder awk

Beitrag von hikaru » 22.01.2015 00:39:28

Du kannst mit sort -k N nach einer bestimmten Spalte sortieren, wobei N die Spaltennummer ist nach der sortiert werden soll.

Text rechtsbündig ausrichten geht mit sed* [1], wenn du die maximale Zeilenlänge kennst (hier 105):

Code: Alles auswählen

sed -e :a -e 's/^.\{1,105\}$/ &/;ta'
Die maximale Zeilenlänge kriegst du z.B. mit wc -L heraus. Dabei gibt wc allerdings noch den Dateinamen mit aus, den du wegschneiden musst (z.B. mit grep).

Als "Einzeiler" in bash könnte das Resultat also so aussehen:

Code: Alles auswählen

export WC=`wc -L DATEINAME | egrep -o '^[0-9]+'`; sort -k 2 DATEINAME | sed -e :a -e 's/^.\{1,'$WC'\}$/ &/;ta'
Mit awk geht das sicher dreimal schöner und kürzer, ber damit stehe ich immer noch auf Kriegsfuß.


*) Ich habe das Beispiel selbst nicht ganz verstanden, da ich mit Scripten und Sprungmarken in sed nicht vertraut bin. Falls das jemand einfach erklären könnte würde ich mich freuen.
[1] http://www-rohan.sdsu.edu/doc/sed.html

Cae
Beiträge: 6349
Registriert: 17.07.2011 23:36:39
Wohnort: 2130706433

Re: Sortieren mit sed oder awk

Beitrag von Cae » 22.01.2015 08:08:45

Code: Alles auswählen

$ awk 'BEGIN{for(i=0;i<9;i++)printf("foo bar%s baz %d test\n", rand()>0.5?" extra":"", 255*rand())}' >testdata
$ awk '{print($(NF-1), $0)}' <testdata
247 foo bar extra baz 247 test
175 foo bar baz 175 test
120 foo bar extra baz 120 test
164 foo bar extra baz 164 test
99 foo bar baz 99 test
26 foo bar baz 26 test
155 foo bar extra baz 155 test
223 foo bar extra baz 223 test
99 foo bar baz 99 test
$ awk '{print($(NF-1), $0)}' <testdata | sort -n
26 foo bar baz 26 test
99 foo bar baz 99 test
99 foo bar baz 99 test
120 foo bar extra baz 120 test
155 foo bar extra baz 155 test
164 foo bar extra baz 164 test
175 foo bar baz 175 test
223 foo bar extra baz 223 test
247 foo bar extra baz 247 test
$ awk '{print($(NF-1), $0)}' <testdata | sort -n | awk '{$1="";gsub(/^ /,"");print}'
foo bar baz 26 test
foo bar baz 99 test
foo bar baz 99 test
foo bar extra baz 120 test
foo bar extra baz 155 test
foo bar extra baz 164 test
foo bar baz 175 test
foo bar extra baz 223 test
foo bar extra baz 247 test
$ 
Das waere der erste Teil. sort -k fuehrt offensichtlich nicht zum Ziel, da es von vorne zaehlt:

Code: Alles auswählen

$ sort -nk 4,5 <testdata 
foo bar extra baz 120 test
foo bar extra baz 155 test
foo bar extra baz 164 test
foo bar extra baz 223 test
foo bar extra baz 247 test
foo bar baz 26 test
foo bar baz 99 test
foo bar baz 99 test
foo bar baz 175 test
$ 
Sofern rev(1) auf Feldern basieren wuerde, koennte man es vor- und nachschalten.

Fuer das rechtsbuendige Ausrichten wuerde ich %s von printf verwenden:

Code: Alles auswählen

$ awk 'BEGIN{printf("%20s\n", "test")}'
                test
$ awk -vcols="$(tput cols)" 'BEGIN{printf("%"cols"s\n", "test")}'
                                                                            test
$ 
In Kombination mit der letzten Version von oben kommt man dann bei

Code: Alles auswählen

$ awk '{print($(NF-1), $0)}' <testdata | sort -n | awk -vcols="$(tput cols)" '{$1="";gsub(/^ /,"");printf("%"cols"s\n",$0)}'
                                                             foo bar baz 26 test
                                                             foo bar baz 99 test
                                                             foo bar baz 99 test
                                                      foo bar extra baz 120 test
                                                      foo bar extra baz 155 test
                                                      foo bar extra baz 164 test
                                                            foo bar baz 175 test
                                                      foo bar extra baz 223 test
                                                      foo bar extra baz 247 test
$ 
heraus.

Gruss Cae
If universal surveillance were the answer, lots of us would have moved to the former East Germany. If surveillance cameras were the answer, camera-happy London, with something like 500,000 of them at a cost of $700 million, would be the safest city on the planet.

—Bruce Schneier

Cae
Beiträge: 6349
Registriert: 17.07.2011 23:36:39
Wohnort: 2130706433

Re: Sortieren mit sed oder awk

Beitrag von Cae » 22.01.2015 08:21:20

hikaru hat geschrieben:Text rechtsbündig ausrichten geht mit sed* [1], wenn du die maximale Zeilenlänge kennst (hier 105):

Code: Alles auswählen

sed -e :a -e 's/^.\{1,105\}$/ &/;ta'
[...]
*) Ich habe das Beispiel selbst nicht ganz verstanden, da ich mit Scripten und Sprungmarken in sed nicht vertraut bin. Falls das jemand einfach erklären könnte würde ich mich freuen.
Das ist eine Schleife. Das s/// setzt ein Space vorne dran, sofern die Anzahl der Zeichen einer Zeile (innerhalb von ^ und $) zwischen 1 und 105 liegt. Sofern eine erfolgreiche Ersetzung stattfindet, wird durch t (springe zum nachfolgenden Label, sofern letztes s/// matchte) zum Label a gesprungen, welches vorher definiert wurde. Da es selbst leer ist, springt die Codeausfuehrung weiter zum s///. Ein an C angelehnter Pseudocode dazu saehe etwa so aus:

Code: Alles auswählen

for each line {
:a
	replace();       // s///
	if has_matched() // t
		goto a;
}
... was eher unschoen aussieht und imho potenziell ineffizient ist.

Gruss Cae
If universal surveillance were the answer, lots of us would have moved to the former East Germany. If surveillance cameras were the answer, camera-happy London, with something like 500,000 of them at a cost of $700 million, would be the safest city on the planet.

—Bruce Schneier

Benutzeravatar
hikaru
Moderator
Beiträge: 13593
Registriert: 09.04.2008 12:48:59

Re: Sortieren mit sed oder awk

Beitrag von hikaru » 22.01.2015 11:05:17

Cae hat geschrieben:sort -k fuehrt offensichtlich nicht zum Ziel, da es von vorne zaehlt:
Ja, die Zählung von hinten hatte ich überlesen.
ret kann laut Manpage keine Felder, nützt also nichts. (Es sei denn das gewünschte Feld enthält nur Palindrome ;) )
Cae hat geschrieben:Das ist eine Schleife. Das s/// setzt ein Space vorne dran, sofern die Anzahl der Zeichen einer Zeile (innerhalb von ^ und $) zwischen 1 und 105 liegt. Sofern eine erfolgreiche Ersetzung stattfindet, wird durch t (springe zum nachfolgenden Label, sofern letztes s/// matchte) zum Label a gesprungen, welches vorher definiert wurde. Da es selbst leer ist, springt die Codeausfuehrung weiter zum s///.
Danke!

uname
Beiträge: 12075
Registriert: 03.06.2008 09:33:02

Re: Sortieren mit sed oder awk

Beitrag von uname » 22.01.2015 14:26:28

Vielleicht nicht schön. Aber so als Idee.
Trennzeichen ist das Leerzeichen. NF-1 bedeutet die zweitletzte Spalte. Irgendwo muss ja die Information stehen.

Code: Alles auswählen

awk '{print $(NF-1) " " $0}' file|sort |awk '{for (i=2;i<=NF;i++) {printf $i " "}printf "\n" }'
Im Prinzip wird die vorletzte Spalte per Leerzeichen getrennt zusätzlich ganz an den Anfang gestellt.
Dann wird sortiert. Und dann wird die erste Spalte wieder weggeworfen ;-)

Bei abweichenden Trennzeichen muss die awk-Option -F verwendet werden. Auch muss die printf-Angabe geändert werden.

Nachtrag:
Leider ist cut wohl nicht in der Lage die Funktion von awk abzubilden. Der Aufruf von z.B.

Code: Alles auswählen

cut -d" " -f3,1-4 file

gibt die Spalten jeweils nur einmal aus. Auch funktioniert die Umsortierung nicht.

Benutzeravatar
Meillo
Moderator
Beiträge: 8818
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Sortieren mit sed oder awk

Beitrag von Meillo » 22.01.2015 21:02:40

uname hat geschrieben: Im Prinzip wird die vorletzte Spalte per Leerzeichen getrennt zusätzlich ganz an den Anfang gestellt.
Dann wird sortiert. Und dann wird die erste Spalte wieder weggeworfen ;-)
Netter Ansatz!

Nachtrag:
Leider ist cut wohl nicht in der Lage die Funktion von awk abzubilden. Der Aufruf von z.B.

Code: Alles auswählen

cut -d" " -f3,1-4 file

gibt die Spalten jeweils nur einmal aus. Auch funktioniert die Umsortierung nicht.
Das hast du richtig erkannt. Die Angabe der Spalten bei cut(1) entspricht der Angabe von Elementen bei mathematischen Mengen: Es gibt keine Reihenfolge, nur vorhanden oder nicht vorhanden.



Das rechtsbuendige Ausrichten wuerde ich als separates Problem angehen und als Filter einfach hinten an die Pipeline anhaengen.

Hier mal ein geschwind zusammen geschriebener Prototyp:

Code: Alles auswählen

awk '
{
	x[NR]=$0
	if (length($0)>max) {
		max=length($0)
	}
}

END {
	for (l in x) {
		printf("%*s\n", max, x[l])
	}
}
'
Use ed once in a while!

Antworten