UNIXwork

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

Kind-Prozesse erstellen mit posix_spawn

01. Dezember 2019

Ich hatte mal eine Anleitung dazu geschrieben, wie mittels fork und exec Kind-Prozesse erstellt und Programme ausgeführt werden. In diesem Artikel möchte ich die Funktion posix_spawn vorstellen, die im Prinzip das gleiche kann und dabei ein paar Vorteile hat.

In Unix werden traditionell Programme ausgeführt, in dem der aktuelle Prozess geforked, also dupliziert, wird und im neuen Kind-Prozess wird ein anderes Programm-Image mittels exec geladen.

Das Problem an der Sache ist, dass fork mehr oder weniger Overhead haben kann, je nach Betriebsystem. Linux ist da recht effizient, es werden aber immer noch die Page Tables des Parent kopiert. Dazu kommt, dass es auch etwas umständlicher zu programmieren ist, als nur eine einzige Funktion aufzurufen.

Daher wurde irgendwann die Funktion posix_spawn eingeführt:

int posix_spawn(pid_t *pid, const char *path,
	   const posix_spawn_file_actions_t *file_actions,
	   const posix_spawnattr_t *attrp,
	   char *const argv[], char *const envp[]);

Um z.B. das Programm /bin/echo in einem separaten Prozess auszuführen, reicht folgender Code:

char *args[3];
args[0] = "echo";
args[1] = "Hello World";
args[2] = NULL;

pid_t pid; // child pid
posix_spawn(&pid, "/bin/echo", NULL, NULL, args, NULL);

Das ist ein sehr einfaches Beispiel. Was man eigentlich noch bräuchte, ist eine Möglichkeit, die Filedescriptoren für stdin, stdout und stderr zu modifizieren. Hierfür gibt es den Parameter file_action. Um den zu verstehen, sollte man sich noch mal anschauen, wie Programme mittels fork und exec ausgeführt werden, daher noch mal eine kleine Zusammenfassung meines Artikels dazu.

Stdin, stdout und stderr sind Filedescriptoren, die immer die Nummern 0, 1 und 2 haben. Mit der Funktion dup2 können wir einen Filedescriptor duplizieren und der Kopie eine exakte Nummer zuweisen. Was man also machen muss ist, vor dem Erstellen des neuen Prozesses erstmal neue Filedescriptoren für stdin/stdout/stderr zu erstellen (z.B. mit pipe oder open) und nach dem Erstellen des Kind-Prozesses aber noch vor exec werden mittels dup2 die Filedescriptoren auf die Positionen 0, 1 und 2 gelegt.

Der file_actions Parameter bei posix_spawn ist genau dafür gedacht. Diesem können Aktionen wie dup2 oder auch close und open zugeordnet werden, die dann nach dem Erstellen des Kind-Prozesses verarbeitet werden.

Zuerst muss file_actions hierfür initialisiert werden:

posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init(&actions);

Danach können Actions zugewiesen werden:

// stderr im Kind-Prozess schließen
posix_spawn_file_actions_addclose(&actions, 2);

// stdout in die Datei out.txt umleiten
posix_spawn_file_actions_addopen(&actions, 1, "out.txt", O_CREAT|O_WRONLY, 0644);

Nach dem Ausführen von posix_spawn sollte der Speicher wieder freigegeben werden:

posix_spawn_file_actions_destroy(&actions);

Ich habe auch ein ausführliches Beispiel geschrieben, in dem die Ausgabe in eine Pipe mit Hilfe von posix_spawn_file_actions_adddup2 umgeleitet wird, die dann im Parent ausgelesen wird.

Autor: Olaf | 0 Kommentare | Tags: unix, c

XNEdit Update 1.0.1

05. April 2019

Hat nicht lange gedauert, da gibt es schon ein kleines Update für XNEdit. Ein schwerer Fehler beim konvertieren von Dateikodierungen musste behoben werden. Wenn eine Datei mit einer anderen Kodierung als UTF-8 gespeichert werden sollte, wurden dabei ein paar Zeichen übersprungen.

Des Weiteren gibt es ein paar Detailverbesserungen, wenn die Locale-Encoding nicht UTF-8 ist.

XNEdit auf Sourceforge

Autor: Olaf | 0 Kommentare | Tags: xnedit, x11, unix, linux

XNEdit - Mein NEdit-Fork mit Unicode-Support

06. März 2019

NEdit dürfte einer der ältesten grafischen Texteditoren für Unix-Systeme sein. Seit 1991 reifte dieses gute Stück Software und hat über die Jahre einige Features angesammelt. Leider ist die Entwicklung in diesem Jahrtausend mehr oder weniger eingeschlafen. Bis 2004 gab es noch regelmäßig neue Releases, danach jedoch praktisch nur noch Bug-Fixes.

Vor allem wurde es verpasst, in NEdit auch Support für Unicode einzubauen, was in der heutigen Zeit aber ein absolut unverzichtbares Feature ist. Auch das X11 Core basierte Text Rendering, welches nur Bitmap-Fonts unterstützt, ist nicht mehr zeitgemäß.

Im Dezember 2018 hatte ich spontan Lust mich dieser Probleme anzunehmen und NEdit so wieder zu einem benutzbaren Editor zu machen. Das ist mir hoffentlich auch ganz gut gelungen. Mit dem neuen Xft-basiertem Textrenderer gibt es endlich Antialiasing und Unicode wird nun auch unterstützt.

Damit ist der erste Meilenstein in meinem Modernisierungsplan erreicht. Weitere Verbesserungen und neue Features sind geplant, aber jetzt veröffentliche ich das erstmal als XNEdit 1.0.

Downloads gibt es hier oder auf Sourceforge. Des Weiteren existiert das Projekt auch auf GitHub.

Autor: Olaf | 3 Kommentare | Tags: xnedit, x11, unix, linux

Unix Rechteverwaltung: setuid, setgid, sticky bit

22. April 2018

Die klassische Unix-Rechteverwaltung dürfte den meisten ein Begriff sein. Es gibt drei Arten von Zugriffsrechten: read, write und execute. Für jede Datei sind diese Rechte jeweils für den User, die Group und Other gesetzt. Macht insgesammt 9 Bits, es gibt jedoch noch drei weitere Bits, nämlich für setuid, setgid und das sticky bit. Die Bedeutung dieser zusätzlichen Rechte hängt teilweise davon ab, um was für eine Art Datei es sich handelt.

setuid

Wenn das setuid-Bit für Executables gesetzt ist, dann wird das Programm mit den Rechten des Dateieigentümers ausgeführt. Dies ist zum Beispiel nötig für su, welches Root-Rechte für seine Funkion benötigt.

setgid

Bei Executables bewirkt das setgid-Bit, ähnlich wie bei setuid, dass Programme die Gruppenrechte des Eigentümers beim Ausführen erhalten.

Das setgid-Bit kann jedoch auch für Verzeichnisse gesetzt werden. Dies bewirkt, dass Dateien, die in diesem Verzeichnis erstellt werden, die Gruppe des Verzeichnisses erhalten, und nicht die Gruppe des Benutzers, der die Datei erstellt.

Sticky Bit

Das Sticky Bit kann auf Verzeichnissen angewendet werden und bewirkt bei diesen, dass die dort enthaltenen Dateien nur von ihrem Besitzer gelöscht werden können. Dies ist sinnvoll bei Verzeichnissen, auf die mehrere Benutzer vollen Zugriff haben, denn ohne Sticky Bit kann jeder enthaltene Dateien löschen.

Beispiel: chmod und ls

Erstellen wir kurz eine Datei:

$ touch file
$ ls -l 
total 0
-rw-r--r-- 1 olaf user 0 Apr 22 12:51 file

setuid:

$ chmod u+s file
$ ls -l
total 0
-rwSr--r-- 1 olaf user 0 Apr 22 12:51 file

setgid:

$ chmod g+s file
$ ls -l
total 0
-rwSr-Sr-- 1 olaf user 0 Apr 22 12:51 file

Sticky Bit:

$ mkdir dir
$ ls -l
total 4
drwxr-xr-x 2 olaf user 4096 Apr 22 12:53 dir
-rwSr-Sr-- 1 olaf user    0 Apr 22 12:51 file
$ chmod +t dir
$ ls -l
total 4
drwxr-xr-t 2 olaf user 4096 Apr 22 12:53 dir
-rwSr-Sr-- 1 olaf user    0 Apr 22 12:51 file
Autor: Olaf | 0 Kommentare | Tags: unix
Zurück Weiter