UNIXwork

Datenanalyse in der Shell Teil 2: Beispiele

11. Dezember 2019

In Teil 1 habe ich einige Unix-Tools für einfache Datenauswertungen vorgestellt. Dies möchte ich in diesem Artikel mit zwei Beispielen etwas vertiefen.

Dafür habe ich mir ein paar Testdaten von hier besorgt, und zwar die Datei airtravel.csv.

"Month", "1958", "1959", "1960"
"JAN",  340,  360,  417
"FEB",  318,  342,  391
"MAR",  362,  406,  419
"APR",  348,  396,  461
"MAY",  363,  420,  472
"JUN",  435,  472,  535
"JUL",  491,  548,  622
"AUG",  505,  559,  606
"SEP",  404,  463,  508
"OCT",  359,  407,  461
"NOV",  310,  362,  390
"DEC",  337,  405,  432

Diese Beispieldaten enthalten für drei Jahre die monatliche Anzahl an Flugreisenden.

Beispiel 1: Summe von Spalten bilden

Als erstes möchten wir die Gesamtsumme pro Jahr wissen, also die drei Spalten aufsummieren. Um die Daten leichter zu verarbeiten, entfernen wir die Tabellenüberschrift sowie die erste Spalte mit den Monatsbezeichnungen.

$ tail -12 airtravel.csv | cut -d ',' -f 2,3,4 > data1.csv
$ cat data1.csv
  340,  360,  417
  318,  342,  391
  362,  406,  419
  348,  396,  461
  363,  420,  472
  435,  472,  535
  491,  548,  622
  505,  559,  606
  404,  463,  508
  359,  407,  461
  310,  362,  390
  337,  405,  432

Wir summieren wir das jetzt? Hierfür benutzen wir das Programm bc, welcher ein Taschenrechner ist, der seine Eingabe von stdin lesen kann. Um eine vernünftige Eingabe für bc zu generieren, müssen wir alle Zeilen einer Spalte zu einer einzigen Zeile zusammenfügen, getrennt durch ein Plus für die Rechenoperation. Dies kann mit dem Tool paste erledigt werden, in dem die -s Option angegeben wird und mit -d ein Trennzeichen.

Zunächst einmal extrahieren wir mit cut eine Spalte und übergeben diese an paste. Dies liefert eine einzige Zeile mit unseren Zahlen, getrennt durch ein +, welche wir an bc übergeben können, um unsere Summe zu erhalten:

$ cut -d ',' -f 1 data1.csv | paste -s -d+ - | bc
4572

Beispiel 2: Maximum und Minimum finden

Als nächstes möchten wir rausfinden, in welchem Monat es im Jahr 1959 am meisten und am wenigsten Reisende gab. Hierfür schneiden wir wieder die Spaltenüberschriften weg. Danach sortieren wir die gewünschte Spalte und holen uns das Minimum und Maximum mit den Tools head und tail:

$ tail -12 airtravel.csv | cut -d ',' -f 1,3 | sort -t ',' -k 2 > col2_sorted.csv
$ cat col2_sorted.csv
"FEB",  342
"JAN",  360
"NOV",  362
"APR",  396
"DEC",  405
"MAR",  406
"OCT",  407
"MAY",  420
"SEP",  463
"JUN",  472
"JUL",  548
"AUG",  559
$ printf "min: %s\nmax: %s\n" "`head -1 col2_sorted.csv`" "`tail -1 col2_sorted.csv`"
min: "FEB",  342
max: "AUG",  559

Fazit: Einfache Aufgaben sind unproblematisch. Bei komplexeren Anforderungen könnte man aber schnell an die Grenzen stoßen. Das war aber noch nicht alles. Im nächsten Artikel warten weitere Werkzeuge auf uns.

Autor: Olaf | 1 Kommentare | Tags: shell, analytics, unix

Extended Attributes Teil 10: Windows Syscalls

10. Dezember 2019

Für den Zugriff auf Alternate Data Streams benötigt man unter Windows keine speziellen Syscalls, da die Auswahl des Streams ein Teil des Dateipfades ist. Daher kann man z.B. auch mit fopen oder vermutlich jeder ähnlichen Funktion in anderen Programmiersprachen auf einen anderen Stream zugreifen.

FILE *f = fopen("test.txt:stream2", "w");
fprintf(f, "Hello ADS\n");    
fclose(f);

Was jedoch nicht so einfach geht ist, eine Liste aller Streams einer Datei zu erhalten. Zum Einsatz kommen dabei die Funktionen FindFirstStreamW und FindNextStreamW. Diese funktionieren ähnlich wie die Funktionen zum Auflisten von Verzeichniseinträgen (FindFirstFileW, FindNextFileW). Hier ein Beispiel, wie die Funktionen benutzt werden:

#include <stdio.h>
#include <windows.h>

int main(int argc, char** argv) {

	WIN32_FIND_STREAM_DATA findData;

	HANDLE h = FindFirstStreamW(L"test.txt", FindStreamInfoStandard, &findData, 0);
	if(h == INVALID_HANDLE_VALUE) {
    	return 1;
	}

	do {
    	printf("%S\n", findData.cStreamName);
	} while(FindNextStreamW(h, &findData));

	return 0;
}

Das Format von cStreamName ist :streamname:$streamtype. Streamtype ist in der Regel immer DATA (ich kenne kein Beispiel, in dem dies nicht der Fall ist). Für den Fall, dass die Datei test.txt in dem Beispiel einen zweiten Stream mit dem Namen mystream hat, wäre die Ausgabe folgendermaßen:

::$DATA
:mystream:$DATA
Autor: Olaf | 0 Kommentare | Tags: windows, xattr

Extended Attributes Teil 9: Windows Commandline Tools

09. Dezember 2019

Windows unterstützt Extended Attributes in Form von Alternate Data Streams (ADS). Ähnlich wie unter Solaris sind Extended Attributes damit keine einfachen Key/Value-Paare, sondern Datei-Streams. Jede Datei hat einen Haupt-Stream mit dem Namen :$DATA. Daneben können aber weitere Streams für eine Datei existieren.

Zugegriffen wird auf ein Attribut bzw. Alternate Data Stream, in dem an den Dateinamen der Stream-Name, getrennt durch einen Doppelpunkt, angehängt wird.

> echo Hello World > test.txt

> type test.txt
Hello World

> echo my attribute value > test.txt:myattribute

> type test.txt:myattribute
my attribute value

Auf den eigentlichen Dateiinhalt kann auch mit dem Stream-Namen :$DATA zugegriffen werde, was auch schon Ursache für Sicherheitslücken war. Den IIS konnte man so austricksen und den Inhalt von ASP-Dateien erhalten, in dem man ::$DATA an die URL angehangen hat.

Da der Stream Teil des Dateipfades ist, kann eigentlich mit jedem beliebigen Programm darauf zugegriffen werden, z.B. auch mit Notepad:

> notepad test.txt:myattribute

Lustigerweise funktioniert dies nicht in den Powershell-Cmdlets. Diese haben jedoch Parameter für den Zugriff auf ADS. Z.B. kann Get-Item alle Streams einer Datei auflisten:

PS D:\> Get-Item -path test.txt -stream *

PSPath        : Microsoft.PowerShell.Core\FileSystem::D:\test.txt::$DATA
PSParentPath  : Microsoft.PowerShell.Core\FileSystem::D:\
PSChildName   : test.txt::$DATA
PSDrive       : D
PSProvider    : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
FileName      : D:\test.txt
Stream        : :$DATA
Length        : 14

PSPath        : Microsoft.PowerShell.Core\FileSystem::D:\test.txt:myattribute
PSParentPath  : Microsoft.PowerShell.Core\FileSystem::D:\
PSChildName   : test.txt:myattribute
PSDrive       : D
PSProvider    : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
FileName      : D:\test.txt
Stream        : myattribute
Length        : 21
Autor: Olaf | 0 Kommentare | Tags: windows, xattr

Datenanalyse in der Shell Teil 1: Basis-Tools

08. Dezember 2019

Teil 1 einer kleinen Tutorial-Serie zum Thema, wie mit Unix-Tools einfache Datenanalysen durchgeführt werden können.

In Diesem Artikel soll es zunächst um einige grundlegende Tools gehen, die einem dabei helfen können, CSV-Dateien oder andere Textdateien zu verarbeiten.

grep

Vermutlich das wichtigste Tool überhaupt und wohl bekannt. Der Vollständigkeit halber möchte ich trotzdem darauf eingehen. Mit grep können Dateien nach einem Suchmuster durchsucht werden.

grep suchmuster datei

Ausgegeben werden alle Zeilen, die dem Suchmuster entsprechen. Das Suchmuster ist eine Regular Expression, worauf ich hier aber nicht im Detail eingehen möchte. Wem regex gar nichts sagt, für den gibt es im Internet tausende Tutorials zu dem Thema.

Möglich ist auch, die Suche umzukehren und alle Zeilen zu erhalten, die nicht dem Muster entsprechen. Hierfür gibt es die Option -v

grep -v suchmuster datei

Eine weitere praktische Option ist -c, womit die betroffenen Zeilen gezählt werden. Die Ausgabe enthält dann nur die Anzahl der Zeilen, die dem Muster entsprechen.

sort

Mit sort können Daten sortiert werden. Dies ist nicht nur für eine schöne Ausgabe wichtig, sondern diverse Tools, die wir uns noch anschauen werden, setzten sortierte Daten voraus.

sort liest Daten von stdin, oder optional aus einer Datei, und gibt die Daten in sortierter Reihenfolge auf stdout aus. Dabei gibt es mehrere Optionen dafür, wie sortiert werden soll. Ebenso ist es möglich, nach einer bestimmten Spalte in den Daten zu sortieren. Dabei wird Space als Standardtrennzeichen zwischen Spalten benutzt.

Ein Beispiel:

$ cat > testdata
13 9
41 3
14 6
12 7
99 5
$ sort testdata
12 7
13 9
14 6
41 3
99 5
sort -k 2 testdata 
41 3
99 5
14 6
12 7
13 9

paste

paste fügt Dateien zeilenweise zusammen

paste [datei1] [datei2] ...

Standardmäßig werden die Zeilen durch einen Tabulator getrennt. Alternativ kann mit der Option -d ein anderes Trennzeichen angegeben werden.

$ cat > col1
project
dav
xnedit
ucx
$ cat > col2
version
1.2.4
1.1.1
2.0
$ paste -d ';' col1 col2
project;version
dav;1.2.4
xnedit;1.1.1
ucx;2.0

uniq

Mit uniq können Duplikate in der Eingabe gefiltert werden. Hierfür müssen die Daten bereits sortiert sein.

$ cat > data1
rhel
ubuntu
fedora
debian
ubuntu
rhel
ubuntu
arch
opensuse
fedora
rhel
debian
$ sort data1 | uniq
arch
debian
fedora
opensuse
rhel
ubuntu

uniq kann auch zählen, wie oft einzelne Zeilen vorkommen:

$ sort data1 | uniq -c
	  1 arch
	  2 debian
	  2 fedora
	  1 opensuse
	  3 rhel
	  3 ubuntu

Bedauerlicherweise ist es nicht möglich anzugeben, dass nur eine bestimmte Spalte betrachtet werden soll. Es können jedoch die ersten Felder übersprungen werden mit der Option -f nfields, wobei nfields die Anzahl der Felder ist, die übersprungen werden sollen.

cut

Mit Hilfe von cut können einzelne Bereiche aus einer Eingabe extrahiert werden. Dies können zum einen einzelne Byte-Ranges sein, aber auch einzelne Felder in tabellarischen Daten.

Felder können mit der Option -f feldliste angegeben werden. Die Feldliste ist entweder die Nummer eines Feldes, oder eine Kommagetrennte Liste an Feldnummern.

Wer ein anderes Trennzeichen als Tabulatoren benötigt, kann dies mit der -d Option angeben.

Ein Beispiel:

$ cat > machines
server1 admin 192.168.1.10
server2 root 192.168.1.20
client1 user1 192.168.1.100
client2 user2 192.168.1.101
$ cut -d ' ' -f 2 machines 
admin
root
user1
user2
bash-5.0$
$ cut -d ' ' -f 1,3 machines 
server1 192.168.1.10
server2 192.168.1.20
client1 192.168.1.100
client2 192.168.1.101

Kombiniertes Beispiel

Zum Abschluss wollen wir das Ganze testen, in dem wir Log-Dateien im Common Log Format analysieren. Angenommen wir haben eine Datei access.log mit folgendem Inhalt:

192.168.1.30 - - [08/Dec/2019:14:10:23 +0100] "GET /index.html HTTP/1.1" 200 5126
192.168.1.20 - - [08/Dec/2019:14:13:15 +0100] "GET /article1.html HTTP/1.1" 200 1876
192.168.1.65 - - [08/Dec/2019:14:14:55 +0100] "GET /index.html HTTP/1.1" 200 5126
192.168.1.30 - - [08/Dec/2019:14:16:01 +0100] "GET /article1.html HTTP/1.1" 200 1876
192.168.1.30 - - [08/Dec/2019:14:17:53 +0100] "GET /blog.css HTTP/1.1" 200 1543
192.168.1.65 - - [08/Dec/2019:14:19:12 +0100] "GET /downloads.html HTTP/1.1" 200 1245
192.168.1.71 - - [08/Dec/2019:14:23:59 +0100] "GET /article1.html HTTP/1.1" 200 1876
192.168.1.30 - - [08/Dec/2019:14:25:21 +0100] "GET /article2.html HTTP/1.1" 200 6362
192.168.1.20 - - [08/Dec/2019:14:26:17 +0100] "GET /index.html HTTP/1.1" 200 5126
192.168.1.30 - - [08/Dec/2019:14:28:05 +0100] "GET /index.html HTTP/1.1" 200 5126
192.168.1.65 - - [08/Dec/2019:14:31:10 +0100] "GET /package.zip HTTP/1.1" 200 65283

Als Beispiel möchten wir ermitteln, wie oft welche Seite aufgerufen wurde. Der Teil, den wir dafür genauer anschauen wollen ist das Feld 7. Die Daten aus diesem Feld erhalten wir mit folgendem Befehl:

$ cut -d ' ' -f 7 access.log

Mit uniq kann das Vorkommen jedes einzelnen Datensatzes gezählt werden. Die Daten müssen dafür jedoch sortiert sein, wofür sort zum Einsatz kommt. Mittels einer Pipe können wir das Ganze kombinieren:

$ cut -d ' ' -f 7 access.log | sort | uniq -c
	  3 /article1.html
	  1 /article2.html
	  1 /blog.css
	  1 /downloads.html
	  4 /index.html
	  1 /package.zip

Als zweites Beispiel möchten wir nachschauen, welche Seiten wie oft von der IP 192.168.1.30 aufgerufen wurden. Hierfür verwenden wir grep, um erst die Daten zu filtern.

$ grep '192\.168\.1\.30' access.log | cut -d ' ' -f 7 | sort | uniq -c
	  1 /article1.html
	  1 /article2.html
	  1 /blog.css
	  2 /index.html

So viel zu diesen Tools. In Teil 2 werden noch weitere Werkzeuge vorgestellt.

Autor: Olaf | 0 Kommentare | Tags: shell, analytics, unix

GraalVM unterstützt jetzt WASM

07. Dezember 2019

GraalVM ist eine erweiterte Java-VM, die neben Java noch diverse andere Sprachen unterstützt. Diese bietet einer bisher unerreichte Interoperabilität. So ist es beispielsweise möglich, von Java aus direkt auf Datenstrukturen oder Funktionen von Python oder JavaScript zuzugreifen.

Neu ist jetzt eine experimentelle Unterstützung für WebAssembly (WASM). WASM ist ein standardisierter Bytecode, der speziell für Browser entworfen wurde. Dieser soll es ermöglichen, dass neben JavaScript noch andere Programmiersprachen für Web-Client-Programmierung genutzt werden können, in dem diese zu WASM-Bytecode kompiliert werden. Dieser Bytecode kann jetzt auch von der GraalVM ausgeführt werden.

Damit kann nun jede Sprache, für die es WASM-Compiler gibt, auch innerhalb der GraalVM genutzt werden.

Wer sich das genauer anschauen möchte, sollte einen Blick in die offizielle Ankündigung werfen.

Autor: Olaf | 0 Kommentare | Tags: graalvm, wasm, java
Zurück Weiter