Nachdem ich neulich ein Video zum Thema AWK gefunden hatte habe ich gedacht - da geht noch mehr
Es ging dabei darum, dass man LibreOffice Calc oder ähnliche Programme nicht benötigt wenn man ein Linux-Terminal hat. Ausgehend von Garys Beispielen habe ich mir eine schnelle Liste von Anforderungen aufgestellt, die ich hier mal mit Syntaxbeispielen zu Lösung darstellen möchte.
Ich bin von einer ähnlichen Datenquelle wie Gary ausgegangen - ein einfaches Dateisystem-Listing mittels ls -l sorgte für Input. Ich musste die erste Zeile noch abschneiden damit ich sauber mit tabellarischen Daten arbeiten konnte - dafür sorgte tail -n +2.
Als erstes wird das Feld mit der Dateigröße in jeder Zeile der Ausgabe ausgegeben:
ls -l /usr/bin |tail -n +2| awk '{print $5}'
Die Anzahl der Zeilen kann man mittels wc ermitteln:
ls -l /usr/bin |tail -n +2| wc -l
Das funktioniert aber auch mit AWK:
ls -l /usr/bin |tail -n +2| awk 'END{print NR}'
Man kann die extrahierten Daten jeder Zeile durch eine vorangestellte Zeilennummer ergänzen:
ls -l /usr/bin |tail -n +2| awk '{print NR":"$5}'
Nun ein wenig Statistik: Die Berechnung des Durchschnitts (Mittelwerts) der Daten in Spalte 5 erfolgt so:
ls -l /usr/bin |tail -n +2| awk 'BEGIN{sum=0.0}{sum+=$5}END{printf("Average: %f\n",sum/NR)}'
Variablen werden von AWK immer auf 0 initialisiert - daher kann man im vorigen Statement die BEGIN Klausel auch weglassen. Es ist auch möglich, Variablen auf der Kommandozeile zu vereinbaren und dem Skript zu übergeben:
ls -l /usr/bin |tail -n +2| awk -v N=5 '{sum+=$N}END{printf("Average: %f\n",sum/NR)}'
Wir berechnen zusätzlich die Standardabweichung:
ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{sum+=$N;sumsq+=$N^2}END{printf("Average: %f Std deviation: %f\n",sum/NR,sqrt((sumsq-sum^2/NR)/NR))}'
Dieses Mal wird der Median bestimmt:
ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk \
'{a[i++]=$1}END{x=int((i+1)/2);if (x < (i+1)/2) y=(a[x-1]+a[x])/2; else y=a[x-1]; printf("Median: %f\n",y)}'
Möchte man erfahren, ab welchem Wert ein bestimmter Prozentsatz der Eingaben jeweils unterhalb dieses Wertes liegen (Percentile), kann man das ebenfalls mittels AWK herausfinden:
ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{print $N}'|sort -n| awk '{s[NR-1]=$N} END{for(i=0.1;i<=1.0;i+=0.1){printf("%d %f\n",i*100,s[int(NR*i-0.5)])}}'
Man kann mittels AWK auch Histogramme erzeugen:
ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk -v DELTA=150000 \
'BEGIN{delta = (DELTA == "" ? 10000 : DELTA)} \
{bucketNr = int(($0+delta) / delta);cnt[bucketNr]++;numBuckets = (numBuckets > bucketNr ? numBuckets : bucketNr)} \
END{for (bucketNr=1; bucketNr<=numBuckets; bucketNr++) {end = beg + delta;printf("%0.1f %0.1f %d\n"), beg, end, cnt[bucketNr];beg = end;}}'>hist.dat
In diesem Fall sollten wir uns auf den interessanten Teil des Histogrammes beschränken - das zeigt auch gleich, dass man mehrere Variablen auf der Kommandozeile vereinbaren kann:
ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk -v DELTA=1000 -v MAX=15001 \
'BEGIN{delta = (DELTA == "" ? 10000 : DELTA)} \
{if($0<15001){bucketNr = int(($0+delta) / delta);cnt[bucketNr]++;numBuckets = (numBuckets > bucketNr ? numBuckets : bucketNr)}} \
END{for (bucketNr=1; bucketNr<=numBuckets; bucketNr++) {end = beg + delta;printf "%0.1f %0.1f %d\n", beg, end, cnt[bucketNr];beg = end;}}'>hist.dat
Man kann dieses Histogramm direkt mit Gnuplot im Terminal darstellen - die Größe des Plots passt sich automatisch der Größe des Terminals an...
ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk -v DELTA=1000 -v MAX=15001 \
'BEGIN{delta = (DELTA == "" ? 10000 : DELTA)} \
{if($0<MAX){bucketNr = int(($0+delta) / delta);cnt[bucketNr]++;numBuckets = (numBuckets > bucketNr ? numBuckets : bucketNr)}} \
END{for (bucketNr=1; bucketNr<=numBuckets; bucketNr++) {end = beg + delta;printf "%0.1f %0.1f %d\n", beg, end, cnt[bucketNr];beg = end;}}' \
|gnuplot -p -e "set terminal dumb size $(tput cols), $(tput lines) enhanced; set autoscale;set style data histogram;set style fill solid;plot '-' using 3:xtic(1)"
Es ist möglich, nur bestimmte Zellen in der Berechnung zu benutzen - Man kann zum Beispiel anhand regulärer Ausdrücke bestimmte Zeilen ausschließen:
ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{if($9 ~ /^m/){sum+=$N;sumsq+=$N^2;count++}} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'
Es ist dabei auch möglich, die Groß- und Kleinschreibung zu ignorieren:
ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{IGNORECASE = 1;if($9 ~ /^m/){sum+=$N;sumsq+=$N^2;count++}} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'
Eine alternative Schreibweise dafür:
ls -l /usr/bin |tail -n +2| awk -v N=5 \
'IGNORECASE = 1 && $9 ~ /^m/{sum+=$N;sumsq+=$N^2;count++} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'
Es ist natürlich auch möglich, die in der Berechnung zu berücksichtigenden Zeilen einfach über ihre Zeilennummern zu bestimmen:
ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{if(NR >9 && NR<21){sum+=$N;sumsq+=$N^2;count++}} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'
Eine alternative Schreibweise dafür:
ls -l /usr/bin |tail -n +2| awk -v N=5 \
'(NR >9 && NR<21){sum+=$N;sumsq+=$N^2;count++} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'
Es ist sogar möglich, Werte über den Vergleich von Daten (Zeitpunkten) zu filtern:
ls -lt --time-style=long-iso /usr/bin|awk -v date=2020-06-09 'date<$6{print $8}'
Es gibt auch Möglichkeiten, Funktionen zu definieren - während int(n) einfach nur die Nachkommastellen abschneidet, kann man in AWK eine korrekte Rundungsfunktion wie folgt definieren:
func round(n)
{
return int(n+0.5)
}
Eine Funktion für die nächstgrößere ganze Zahl ist ebenfalls sehr einfach zu schaffen:
function ceil(n)
{
return n%1 ? int(n)+1 : n
}
Wer möchte, kann sich das hier angehängte Cheat-Sheet, das ich aus einem Template erstellt habe auch ausdrucken...
ls -l /usr/bin |tail -n +2| \
awk '{if($5>999999)print NR"\t"$1"\t"$2"\t"$3"\t"$4"\t\033[1;31m"$5"\033[0m"; \
else print NR"\t"$1"\t"$2"\t"$3"\t"$4"\t"$5;}'
Das Cheat-Sheet wurde natürlich entsprechend erweitert...
02.11.2021
Durch einen Post auf Mastodon wurde ich auf eine Idee gebracht...
29.10.2021
Ich habe bereits darüber berichtet, dass man in vielen Fällen AWK den Vorzug vor Excel und ähnlichem geben kann (und sollte!) - hier nun einige weitere Inspirationen dahingehend
26.05.2021
Ich bin neulich bei Mastodon über eine Anfrage gestolpert, wie man online schnell eine graphische Darstellung statistischer Daten mit Mittelwert usw. anfertigen könnte. Ich gab dazu einige Suchparameter ergänzt um den Schlüsselbegriff "Gnuplot" ein und fand sofort einen Artikel, der erklärte, wie man das angefragte Szenario mittels Gnuplot umsetzen könnte.
15.01.2021
Nachdem ich neulich mein AWK-Kochbuch um ein neues Rezept erweitert habe mit dem es möglich ist, einzelne Felder einer Datei regelbasiert mittels Ansi-Escape-Sequenzen unterschiedlich einzufärben, hat mich der Ansi-Steuercode-Virus wieder voll erwischt...
Multi-User-WebDAV, Docker, GitHub
17.11.2019
Nachdem ich mich in letzter Zeit verstärkt mit Docker und dem zugehörigen Ökosystem beschäftige, habe ich begonnen, verschiedenste Dienste in Containern zu testen um zu sehen, ob in manchen Fällen LXC oder KVM nicht doch die bessere Wahl wäre...
Weiterlesen...Android Basteln C und C++ Chaos Datenbanken Docker dWb+ ESP Wifi Garten Geo Go GUI Gui Hardware Java Jupyter Komponenten Links Linux Markdown Markup Music Numerik OpenSource PKI-X.509-CA Python QBrowser Rants Raspi Revisited Security Software-Test sQLshell TeleGrafana Verschiedenes Video Virtualisierung Windows Upcoming...
In eigener Sache...
Weiterlesen...Ich habe eine neue Java Swing Komponente erstellt: Es handelt sich um einen Wrapper für von JToolBar abgeleitete Klassen, die die Werkzeugleiste minimieren und sie nur dann einblenden, wenn der Mauszeiger über ihnen schwebt.
Weiterlesen...Ich habe bereits in einem früheren Artikel über meine ersten Erfolge berichtet, der sQLshell auf Basis des bestehenden Codes aus dem Projekt EBMap4D eine bessere Integration für Geo-Daten zu spendieren und entsprechende Abfragen, bzw. deren Ergebnisse auf einer Kartenansicht zu visualisieren.
Weiterlesen...Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.
Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.
Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...
PS: Meine öffentlichen Codeberg-Repositories findet man hier.