bash: eigene Sortierfunktion?

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

bash: eigene Sortierfunktion?

Beitrag von heisenberg » 21.07.2017 20:11:41

Hallo zusammen,

eine Frage zum fortgeschrittenen sortieren in der Bash.

Ich finde sort nicht gerade besonders trivial und wartungsfreundlich bei komplexeren suchen. Die Methode, die Programmiersprachen anbieten gefällt mir da viel besser, als die da wäre:

Schreibe eine Funktion, die entweder 0 bei Gleichheit, 1 wenn der erste Wert grösser ist und -1 wenn der 2. Wert grösser ist.

Beispiel in PHP:

Code: Alles auswählen

<?php
function cmp($a, $b)
{
    return strcmp($a["frucht"], $b["frucht"]);
}

$fruechte[0]["frucht"] = "Zitronen";
$fruechte[1]["frucht"] = "Aepfel";
$fruechte[2]["frucht"] = "Orangen";

usort($fruechte, "cmp");

while (list($key, $value) = each($fruits)) {
    echo "\$fruechte[$key]: " . $value["frucht"] . "\n";
}
?>
Weiss jemand, ob man so etwas auch irgendwie in bash scripten kann?

Die Aufgabe

Ausgangsdaten

Code: Alles auswählen

  46.165.224.87 Status: FEHLER   421 Too many concurrent SMTP connections; please try again later.
  209.58.136.65 Status: OK       220 mx59.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221
138.201.140.156 Status: OK       220 quarantine2.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221
  199.115.117.7 Status: FEHLER   421 mx22.antispamcloud.com: Too much load; please try again later
 95.211.160.147 Status: OK       220 master.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221 
Sortierregeln
  1. Oberste Sortierebene(Status): FEHLER > OK
  2. Zweite Sortierebene(Servertyp): master > mx > quarantine
  3. Dritte Sortierebene(Serverindex): numerisch
  4. Werte die nicht vorhanden sind, sind mit unendlich gleichzusetzen(Sprich grösstmöglicher Wert; Siehe Zeile 1 Ausgangsdaten, Zeile 2 Ergebnisdarstellung)
  5. Komplette Gleichheit - die auftreten kann bei nicht vorhandenen Werten - kann ignoriert werden
Definition der Felder

209.58.136.65 Status: OK 220 mx59.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221
  • Status OK
  • Servertyp mx
  • Serverindex 59
Gewünschtes Ergebnis

Code: Alles auswählen

  199.115.117.7 Status: FEHLER   421 mx22.antispamcloud.com: Too much load; please try again later
  46.165.224.87 Status: FEHLER   421 Too many concurrent SMTP connections; please try again later.
 95.211.160.147 Status: OK       220 master.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221 
  209.58.136.65 Status: OK       220 mx59.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221
138.201.140.156 Status: OK       220 quarantine2.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221
Ich habe das schon in PHP implementiert. Mich würde nur interessieren ob's mit Bash auch geht, bzw. ob's mit sort nicht vielleicht doch viel einfacher als gedacht geht.

Das ist die Sortierfunktion in PHP:

Code: Alles auswählen

function mysort($a,$b) {
        list ($a_status,$a_code,$a_server_type,$a_servername_index) = parse_line($a);
        list ($b_status,$b_code,$b_server_type,$b_servername_index) = parse_line($b);

        if   ( $a_status != $b_status ) {
                $res = ($a_status == "OK") ? 1 : -1;

        } else {
                if ( $a_server_type != $b_server_type ) {
                        if($a_server_type=="master")                                    $res = -1;
                        if($a_server_type=="mx" && $b_server_type=="quarantine")        $res = -1;
                        if($b_server_type=="master")                                    $res =  1;
                        if($b_server_type=="mx" && $a_server_type=="quarantine")        $res =  1;
                } else {
                        $res = ( $a_servername_index > $b_servername_index ) ? 1 : -1;
                        }
                }
        return $res;
}
Grüße,
h.

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

Re: bash: eigene Sortierfunktion?

Beitrag von Meillo » 22.07.2017 07:02:03

Fuer mich ist sort(1) auch eine Herausforderung. Hier aber mal ein paar Anmerkungen, die dir vielleicht weiterhelfen.

Es ist mir sort(1) nicht moeglich, halbe Woerter zu sortieren. Wenn du also ``mx'' und ``59'' separat sortieren willst, dann musst du sie zuvor auftrennen.

Was moeglich ist, ist verschiedene Spalten/Felder nach unterschiedlichen Kriterien zu sortieren. Dabei hast du eine Handvoll zur Auswahl, wie alphabetisch, numerisch, Monatsnamen, Versionsnummern ... je nach Implementierung. Sonderwuensche, wie du das beschreibst, sind AFAIK nicht umsetzbar. (Was ueber ein externes Tool a la strcmp zu loesen, waere wohl auch zu ineffizient (gewesen), darum gibt es die Moeglichkeit nicht, vermute ich.)

Alle deine Anforderungen kann man mit sort(1) nicht umsetzen, aber zu 90% kommst du an dein Wunschziel heran (teilweise durch glueckliche Zufaelle):

Code: Alles auswählen

sort -k3,3 -k5,5
(In deinem Beispiel wird dadurch die gewueschte Ausgabe erzeugt.)
Use ed once in a while!

breakthewall
Beiträge: 507
Registriert: 30.12.2016 23:48:51

Re: bash: eigene Sortierfunktion?

Beitrag von breakthewall » 22.07.2017 07:36:59

Beispiel:

Code: Alles auswählen

wake@010010:~$ sort -k 3,3 -k 5,5 testfile
  199.115.117.7 Status: FEHLER   421 mx22.antispamcloud.com: Too much load; please try again later
  46.165.224.87 Status: FEHLER   421 Too many concurrent SMTP connections; please try again later.
 95.211.160.147 Status: OK       220 master.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221 
  209.58.136.65 Status: OK       220 mx59.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221
138.201.140.156 Status: OK       220 quarantine2.antispamcloud.com ESMTP Exim 4.89-119784 Fri, 21 Jul 2017 18:44:59 +0200221
Damit bekomme ich genau jenes Ergebnis was haben wolltest. Zunächst wird Feld 3 und Feld 5 natürlich sortiert, und dann natürlich numerisch sortiert von groß bis klein. Die natürliche Sortierung schliesst auch das gewünschte Format, "master > mx > quarantine" automatisch mit ein. Und für weitere Abweichungen, lässt sich für jedes Feld auch eine eigene Sortierung im Detail definieren. Daher ausprobieren ob das soweit passt, auch für größere und vielfältigere Datenmengen.

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: bash: eigene Sortierfunktion?

Beitrag von heisenberg » 22.07.2017 11:04:26

breakthewall hat geschrieben:Und für weitere Abweichungen, lässt sich für jedes Feld auch eine eigene Sortierung im Detail definieren.
Wie meinst Du das? Meinst Du das was Meillo geschrieben hat? (alphabetisch, numerisch, Monatsnamen, Versionsnummern ... )

---

Das master,mx und quarantine in dem Fall alphabetisch sortierbar sind dass macht das ganz natürlich einfach. Den Index dahinter muss ich natürlich separat sortieren, da eine alphabetische Sortierung falsch wäre(mx9 grösser als mx10, da Zeichen "9" grösser als Zeichen "1"). In meinen Beispieldaten ist so ein Fall nicht enthalten.

---

Ja sieht so aus, als ob es mit eigene Sortierung dann mit Bash nicht geht. Vielleicht kann man mal ein Bash-usort schreiben. :) Wenn auf externe Pogrammaufraufe verzichtet wird und das nur mit Bash-funktionen realisiert wird, vielleicht ist das dann halbwegs effizient.

---

Was ich an der Bash-Variante auch noch tendenziell fehleranfällig finde, ist die Tatsache dass man die Daten verändern muss, nur um zu sortieren. Anschliessend muss man die Originaldaten wieder zusammen basteln und macht das hoffentlich richtig.

breakthewall
Beiträge: 507
Registriert: 30.12.2016 23:48:51

Re: bash: eigene Sortierfunktion?

Beitrag von breakthewall » 22.07.2017 15:40:06

heisenberg hat geschrieben: ↑ zum Beitrag ↑
22.07.2017 11:04:26
Wie meinst Du das? Meinst Du das was Meillo geschrieben hat? (alphabetisch, numerisch, Monatsnamen, Versionsnummern ... )
Ja.
heisenberg hat geschrieben: ↑ zum Beitrag ↑
22.07.2017 11:04:26
Ja sieht so aus, als ob es mit eigene Sortierung dann mit Bash nicht geht. Vielleicht kann man mal ein Bash-usort schreiben. :) Wenn auf externe Pogrammaufraufe verzichtet wird und das nur mit Bash-funktionen realisiert wird, vielleicht ist das dann halbwegs effizient.
Bislang wurde mit der Bash noch gar nichts getan. Denn sort ist ein eigenes C-Programm, sehr effizient und auch parallelisierbar. Sicherlich ist so etwas auch mit Aufwand ohne Zusatzprogramme möglich, nur letztlich gibt es ja gerade deshalb all die Programmiersprachen, um solche Szenarien einfacher umzusetzen. Spontan würde ich im Bezug auf die Bash, an eine while-Schleife denken, mit multiplen Arrays und etlichen Test-Konditionen, um so etwas anzugehen. Über Arrays lassen sich Daten auch bequem hin und her schieben, auch anhand von Inhalt, Menge und Position selektieren. Daher einfach mal anfangen, den Rest kann man ggf. ergänzen.
heisenberg hat geschrieben: ↑ zum Beitrag ↑
22.07.2017 11:04:26
Was ich an der Bash-Variante auch noch tendenziell fehleranfällig finde, ist die Tatsache dass man die Daten verändern muss, nur um zu sortieren. Anschliessend muss man die Originaldaten wieder zusammen basteln und macht das hoffentlich richtig.
Welche Bash-Variante? Es gab noch keinen reinen Bash-Code. Und das Programm sort verändert die Daten auch nicht.

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: bash: eigene Sortierfunktion?

Beitrag von heisenberg » 24.07.2017 09:22:38

breakthewall hat geschrieben: ↑ zum Beitrag ↑
22.07.2017 15:40:06
Bislang wurde mit der Bash noch gar nichts getan. Denn sort ist ein eigenes C-Programm, sehr effizient und auch parallelisierbar. Sicherlich ist so etwas auch mit Aufwand ohne Zusatzprogramme möglich, nur letztlich gibt es ja gerade deshalb all die Programmiersprachen, um solche Szenarien einfacher umzusetzen. Spontan würde ich im Bezug auf die Bash, an eine while-Schleife denken, mit multiplen Arrays und etlichen Test-Konditionen, um so etwas anzugehen. Über Arrays lassen sich Daten auch bequem hin und her schieben, auch anhand von Inhalt, Menge und Position selektieren. Daher einfach mal anfangen, den Rest kann man ggf. ergänzen.
Mein Wunsch hinter der Frage ist: Geht's mit Bash auch einfach und schnell? Das sehe ich so nicht. Deswegen ist für mich das Thema errst einmal erledigt.
heisenberg hat geschrieben: ↑ zum Beitrag ↑
22.07.2017 11:04:26
Was ich an der Bash-Variante auch noch tendenziell fehleranfällig finde, ist die Tatsache dass man die Daten verändern muss, nur um zu sortieren. Anschliessend muss man die Originaldaten wieder zusammen basteln und macht das hoffentlich richtig.
Welche Bash-Variante? Es gab noch keinen reinen Bash-Code. Und das Programm sort verändert die Daten auch nicht.
Der vorliegende Fall ist ja derart, dass man das Feld des Servernamens nicht auf zweierlei Arten ohne weiteres mit sort sortieren kann. Deswegen müsste man die Daten verändern, wenn man das mit sort machen möchte. Z. B. durch einfügen eines Leezeichen zwischen die unterschiedlichen Sortierfelder "mx[leerzeichen hier]22". Oder geht das ohne?

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

Re: bash: eigene Sortierfunktion?

Beitrag von Meillo » 24.07.2017 18:52:11

heisenberg hat geschrieben: ↑ zum Beitrag ↑
24.07.2017 09:22:38
Der vorliegende Fall ist ja derart, dass man das Feld des Servernamens nicht auf zweierlei Arten ohne weiteres mit sort sortieren kann. Deswegen müsste man die Daten verändern, wenn man das mit sort machen möchte. Z. B. durch einfügen eines Leezeichen zwischen die unterschiedlichen Sortierfelder "mx[leerzeichen hier]22". Oder geht das ohne?
Nur falls es eine fixe Zeichenanzahl waere, dann kannst du mit -k5.3,5n numerisch nach Feld fuenf ab dessen dritten Zeichen sortieren.

Siehe: http://pubs.opengroup.org/onlinepubs/96 ... _20_119_13

Dein Fall, wo die Namen vor der Zahl unterschiedlich lang sind, ist damit aber nicht abgedeckt.

Ich denke, dass dein Fall zu komplex und zu speziell fuer sort(1) ist. Ich wuerde darum deine PHP-Implementierung (die du ja schon fertig hast) einfach als Shebang-Script in ~/bin ablegen. Damit musst du nur den Startup-Overhead in Kauf nehmen.


Edit:
Wenn nur ``mx'' und ``quarantine'' numerische Suffixe haben, koennte so ein Ansatz helfen:

Code: Alles auswählen

sort -k3,3 -k5.1,5.2 -k5.3,5n -k5.11,5n
Ist zwar nicht gerade elegant, tut aber seinen Job recht gut (aufgrund ein paar gluecklicher Eigenschaften der Daten). :-)
Zuletzt geändert von Meillo am 24.07.2017 19:00:23, insgesamt 1-mal geändert.
Use ed once in a while!

Antworten