bash_4

Setup

Hier erzeugen wir ein paar Text-Dateien, die wir später brauchten:

In [338]:
echo "France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand" > countries.txt
In [339]:
echo "Germany
Austria
Poland" > countries_1.txt
In [340]:
echo -e "1618391758\t192.168.0.140\tindex.html
1618391759\t192.168.0.120\tmy_test.html
1618391762\t192.168.0.140\tlogin.html
1618391765\t192.168.0.140\tbla.html
1618391771\t192.168.0.10\tblub.html" > logfile-example.log
In [341]:
echo -e "192.168.0.10\tsokrates
192.168.0.120\taristoteles
192.168.0.140\tplaton" > rechnernamen.txt

Text(strom)bearbeitung und -extraktion

Einstieg in die Bash-Shell - Teil 4

Werkzeuge zur Textbearbeitung

Nach der Unix-Philosophie sind Textströme eine universelle Schnittstelle. Daher gibt es viele Unix-Werkzeuge, die Textströme verarbeiten und manipulieren können. Im folgenden werden die wichtigsten Werkzeuge vorgestellt.

cat

Mit cat kann man (Text-)Dateien (auch) konkatenieren, d.h. unter Beibehaltung der Reihenfolge zusammenfügen. Dabei muss man lediglich mehrere Dateien als Argumente angeben.

In dem folgenden Beispiel zusätzlich mit Process Substitution, um die Grenzen der ursprünglichen Dateien anzuzeigen.

In [7]:
cat countries.txt <(echo ---------) countries_1.txt

# dies könnte natürlich auch wieder als neue Datei geschrieben werden.
# cat countries.txt countries_1.txt > merged_countries.txt
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand
---------
Germany
Austria
Poland

cut

Mit cut kann man "Spalten" (Felder) aus einer Textdatei extrahieren.

In [9]:
# die Zeitstempelangaben können mit `date` für Menschen übersetzt werden:
date -d @1618391759
Mi 14. Apr 11:15:59 CEST 2021
In [10]:
# extrahiere das 1. und 3. Feld (Spalte) mit cut
# Das Standard Feld-Trennzeichen ist TAB, mit
#   -d kann man andere Trennzeichen verwenden.
#   -f gibt die Felder an, die man extrahieren will.
cut logfile-example.log -d $'\t' -f1,3
1618391758	index.html
1618391759	my_test.html
1618391762	login.html
1618391765	bla.html
1618391771	blub.html

Hier ist das Trennzeichen (delimiter) über den Schalter -d mittels ANSI C Quoting explizit auf TAB (Tabulator \t) gesetzt.

sort

Mit Hilfe von sort lassen sich Dateien sortieren, z.B. mit den Schaltern (siehe Manpage)

-t, --field-separator=SEP
    use SEP instead of non-blank to blank transition

-k, --key=KEYDEF
    sort via a key; KEYDEF gives location and type

Sehen Sie sich z.B. die Manpage für die weiteren Schalter an.

Beispiel:

In [12]:
# Sortieren nach dem zweiten Feld (der zweiten Spalte):
sort -t $'\t' -k2 logfile-example.log
1618391771	192.168.0.10	blub.html
1618391759	192.168.0.120	my_test.html
1618391765	192.168.0.140	bla.html
1618391758	192.168.0.140	index.html
1618391762	192.168.0.140	login.html

join

Mit join lassen sich Dateien, die ein gemeinsames Datenfeld haben, verbinden.

Z.B. mit einer Datei mit IP-Rechnernamen Zuweisung, können wir das sortierte log-File (in Process Substitution) mit den Rechnernamen verbinden:

In [278]:
join -1 2 -2 1 -t $'\t' <(sort -t $'\t' -k2 logfile-example.log) rechnernamen.txt
192.168.0.10	1618391771	blub.html	sokrates
192.168.0.120	1618391759	my_test.html	aristoteles
192.168.0.140	1618391765	bla.html	platon
192.168.0.140	1618391758	index.html	platon
192.168.0.140	1618391762	login.html	platon

Der Schalter -1 2 gibt an, dass von der ersten Datei (hier sortiertes Logfile) das zweite Feld zum Zusammenfügen verwendet werden soll. Analog bedeutet -2 1 von der zweiten Datei (rechnernamen.txt) das erste Feld.

Beachten Sie, dass mittels Process Substitution <(sort -t $'\t' -k2 logfile-example.log), die Datei logfile-example.log sortiert wird.

tr

tr übersetzt (translate) Zeichen oder löscht diese.

Beispiel alle Leerzeichen "" zu Tabs und alles Großgeschriebene zu Kleingeschrieben.

In [279]:
tr ' [:upper:]' '\t[:lower:]' < countries.txt
france
canada
burkina	faso
democratic	republic	of	the	congo
russia
new	zealand

Hinweis:

cut funktioniert nicht bei Trennzeichen, die hintereinander vorkommen, aber keine neuen Felder bestimmen, z.B.:

In [285]:
# Hier gibt es mehrere Spaces zwischen den Feldern:
echo -e "1618391758 192.168.0.140 index.html
1618391759   192.168.0.120  my_test.html
1618391762  192.168.0.140  login.html
" > logfile-example_.log
In [286]:
cut logfile-example_.log -d' ' -f1,3
1618391758 index.html
1618391759 
1618391762 192.168.0.140

tr kann hier Abhilfe schaffen. Mit dem Schalter -s (squeeze) werden wiederholte Instanzen eines Zeichens zu einem Zeichen.

In [287]:
# mehrere hintereinanderfolgende "Whitespace" zu einem Zeichen 
tr -s '[:space:]' < logfile-example_.log  | cut -d' ' -f1,3
1618391758 index.html
1618391759 my_test.html
1618391762 login.html

tee

Mit Hilfe des Kommando tee wird die Standard-Eingabe (stdin) in die Standard-Ausgabe geschrieben und zusätzlich in eine Datei. tee ist somit ein Verzweigung, die den Input verdoppelt.

Beispielsweise wird hier zusätzlich zur Ausgabe auf der Kommandozeile das Listing in eine Datei geschrieben:

ls | tee inhalt_des_verzeichnisses.txt

Beispiel für eine sinnvolle Anwendung

Modifiziere "crontab" Datei mit sed (siehe unten) und erstelle Backup in einem Schritt

crontab -l | tee crontab-backup.txt | sed 's/old/new/' | crontab -

  • Das - bei crontab Kommando steht für die Pseudodatei stdin siehe man crontab.
  • Der Cron-Dämonenprozess dient dazu zu regelmäßigen Zeitpunkten Jobs, Skripte etc. automatisch auszuführen. Diese cron-Jobs werden in der crontab-Datei konfiguriert, mehr siehe z.B. in der Ubuntu-Cron Dokumentation oder in Linux-Praxis: zeitbezogene Kommandos.
  • Die crontab-Datei sollte nicht direkt modifiziert werden, sondern über den Befehl crontab

uniq

Mit uniq können Duplikat-Zeilen, die direkt aufeinanderfolgen, beseitigt werden. Eventuell muss die Datei vorher sortiert werden.

Mit dem Schalten -c kann außerdem gezählt, werden wiehäufig die einzelnen Duplikate vorkommen.

   -c, --count
          prefix lines by the number of occurrences

Für die weiteren Schalter sehen Sie sich z.B. die Manpage an.

In [299]:
# Erzeuge eine Datei mit Duplikaten:

# zweimal countries für Duplikate und extra France
cat countries.txt countries_1.txt countries.txt <(echo France) | tee duplicate_countries
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand
Germany
Austria
Poland
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand
France
In [300]:
cat duplicate_countries | sort | uniq -c
      1 Austria
      2 Burkina Faso
      2 Canada
      2 Democratic Republic of the Congo
      3 France
      1 Germany
      2 New Zealand
      1 Poland
      2 Russia

paste

paste führt Daten aus mehreren Dateien spaltenweise zusammen.

Beispiele aus https://wiki.ubuntuusers.de/paste/:

In [301]:
#mit 
seq 1 4
1
2
3
4
In [302]:
paste <(seq 1 5) <(seq 11 15) <(seq 21 25)
1	11	21
2	12	22
3	13	23
4	14	24
5	15	25
In [20]:
paste -s <(seq 1 5) <(seq 11 15) <(seq 21 25)
1	2	3	4	5
11	12	13	14	15
21	22	23	24	25

So kann man dies für eine Addition nutzen:

In [21]:
echo $(paste -sd+ <(seq 1 5)) 
paste -sd+ <(seq 1 5) | bc
1+2+3+4+5
15

nl

nl erzeugt eine Nummerierung der Zeilen.

In [306]:
whatis nl 
echo
nl countries.txt
nl (1)               - number lines of files
nl (1posix)          - line numbering filter

     1	France
     2	Canada
     3	Burkina Faso
     4	Democratic Republic of the Congo
     5	Russia
     6	New Zealand

tac

Gibt eine oder mehere Dateien in ungekehrter Reihenfolge aus:

column

Mit column kann man (unter anderem) an Trennzeichen Spalten erzeugen:

In [342]:
whatis column
echo
head -4 /etc/passwd
echo
echo columnized with column:
head -4 /etc/passwd | column -t -s :
column (1)           - columnate lists

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin

columnized with column:
root    x  0  0  root    /root      /bin/bash
daemon  x  1  1  daemon  /usr/sbin  /usr/sbin/nologin
bin     x  2  2  bin     /bin       /usr/sbin/nologin
sys     x  3  3  sys     /dev       /usr/sbin/nologin

colrm

z.B. x-tes bis y-te Zeichen einer Zeile ausschneiden (Zeichen sind hier jeweils "Spalten"):

od

Dump einer Datei, z.B. im Oktal-Format.

In [316]:
whatis od # ähnlich zu hexdump
od -o longlist
od (1)               - dump files in octal and other formats
od (1posix)          - dump files in various formats
0000000 071554 026440 005154
0000006

split

Aufteilen von einer großen Datei in Teile, z.B. zum Versenden in Emails. Mit cat (concatenate) lässt sie sich wieder rekonstruieren.

In [318]:
whatis split
#rm xa*
split (1)            - split a file into pieces
split (1posix)       - split files into pieces
In [325]:
split -b 20 countries.txt

ls x* # so heißen die Dateien per default
xaa  xab  xac  xad
In [327]:
# Anzeigen der Dateien 
for file in xa*; do
    echo "@"
    cat $file
    echo "#"
done

echo -------------------

# Zusammensetzen der Teile mit cat (geht auch bei binary files)
cat xa*
@
France
Canada
Burkin#
@
a Faso
Democratic Re#
@
public of the Congo
#
@
Russia
New Zealand
#
-------------------
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand

Stream-Editor sed

Mit Hilfe des Stream-Editors sed kann ein (Zeichen-)Datenstrom nach Regeln verändert werden. Meistens verwendet man sed für Ersetzungen mit regulären Ausdrücken.

Beispiel substitute expressions s: sed -E 's/search-pattern/replacement-string/g'

  • g: steht für global, d.h. das nicht nur das erste gefundene Muster ersetzt wird, sondern alle.
  • -E für extended regular expressions
In [328]:
# Ersetze alle Leerzeichen durch ein underscore `_`
# Mehrere Leerzeichen hintereinander nur mit einem _
cat countries.txt | sed -E 's/[[:space:]*]/_/g'
France
Canada
Burkina_Faso
Democratic_Republic_of_the_Congo
Russia
New_Zealand
In [27]:
# Beispiel: Schreibe Fehlerausgabe eines Kommandos zusätzlich in eine log-Datei
# Die log-Datei soll das Datum (in iso format) im Namen haben (ohne Leerzeichen!)
# hier mit echo "..." um es sichtbar zu machen
echo "command 2> $(date -Iseconds | sed -E 's/[[:space:]*]/_/g')_command_errors.log"
command 2> 2021-11-07T13:39:32+01:00_command_errors.log

Gefundene Muster in Ersetzung nutzen

Gefundene Muster können natürlich auch in der Ersetzung genutzt werden. Dazu muss man redex-Gruppen bilden. Diese kann man dann referenzieren: \1 für den Match der ersten Gruppe, \2 für den Match der zweiten Gruppe etc.

Beispiel mit nur einer Gruppe (...):

In [8]:
cat countries.txt | sed -E 's/(a.a)/a\1a/g'
France
Caanaada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zeaalaand

greedy

Reguläre Ausdrücke sind standardmäßig greedy, d.h. sie entsprechen der längst möglichen Zeilenfolge.

Beispiel:

Wir möchten HTML-Tags aus Text beseitigen.

In [330]:
var="Das em-Tag markiert in HTML den Text <em>kursiv</em>."

Dazu verwenden wir folgendes Muster:

In [331]:
echo $var | sed -E 's/<.+>//g'
Das em-Tag markiert in HTML den Text .

Das greedy-Verhalten beseitigt auch den Text zwischen den HTML-Tags, was in unserem Beispiel ungewünscht ist. Daher müssen wir das Muster modifizieren:

In [30]:
echo $var | sed -E 's/<[^>]+>//g'
Das em-Tag markiert in HTML den Text kursiv.

Machen Sie sich klar, was das regex-Muster bedeutet.

Eine andere Möglichkeit ist es, das Muster lazy zu machen. Dann wird der minimale Teil des Suchstrings ausgewählt, auf der das Muster zutrifft, Hierfür dient ein ? im Muster. Das geht aber nicht mit sed.

Auf der Kommandozeile bietet sich alternativ perl (eine Programmiersprache) an:

In [336]:
echo $var | perl -pe 's/<.+?>//g'

# -e lese den Perl-Code direkt von stdin und nicht von einer Datei
# -p prints to stdout, for details see https://www.perl.com/pub/2004/08/09/commandline.html/
Das em-Tag markiert in HTML den Text kursiv.

Zusammenfassung: Greedy vs. Lazy

  • Greedy: Trifft auf die längst-mögliche Zeichenfolge zu.
  • Lazy: Trifft auf die kürzest-mögliche Zeichenfolge zu.

Aufgabe

Schreiben Sie einen Einzeiler (verknüpfte Befehle), in dem Sie alle URLs aus einer Textdatei erkennen und zeilenwiese ausgeben. Machen Sie vereinfachte Annahmen über das Muster einer URL, z.B. das diese immer mit http:// bzw. https:// beginnt. Die URLs sollen zudem immer von Leerzeichen bzw. Zeilenumbrüchen vom Rest des Textes getrennt sein.

  • Hinweis: sed unterstüzt nicht ein optionales Zeichen über ?. Hier müssen Sie auf range {0,1} ausweichen.
  • mit sed -E /pattern/!d lassen sich alle Zeilen löschen, die nicht auf das Pattern zutreffen.
In [344]:
cat text_with_urls.txt
# Kopieren Sie z.B. den Text in eine Datei zu testen Ihrer Lösung
Es gibt im Internet viele Adressen, wie z.B. http://wikipedia.de oder http://christianherta.de/
In dieser Zeile steht keine URL.
Aber es ist auch eine Angabe mit Portnummer erlaubt, wie http://localhost:8080/mein/pfad oder mit einem Usernamen http://benutzername@www.mydomain.de/seite/beispiel.php für 
Durch Textmarken kann man direkt auf Bereiche springen, wie z.B. http://www.mydomain.de/seite//beispiel.php#textmarke
Wieder keine Zeile ohne URL.
Wieder keine Zeile ohne URL. Dies soll bei der Extraktion ignoriert werden (auch keine Leerzeile!)
Statt Servernamen, die per DNS aufgelöst werden, kann aber auch direkt eine IP Adresse stehen: https://192.168.0.1/seite/beispiel.php?vname=christian&ort=berlin Bei dieser Adresse werden noch URL Parameter am Ende übergeben (http get). Der Query String beginnt dabei mit einem Fragezeichen "?" und die Parameter haben die Form name=wert und sind mit "&" getrennt.
Wieder keine Zeile ohne URL.
Diese soll bei der Ausgabe ignoriert werden (auch keine Leerzeile!).

Extraktion

Wenn man mit sed Teile extrahieren will, kann man die Zeile mit den entsprechenden Gruppenreferenzen ersetzen.

Hier mit \1 für die erste regEx-Gruppe:

In [346]:
line="Das ist eine IPv4 Adresse 192.168.0.5 in einem privaten Netz."

# Extract the IPv4
echo $line | sed -n -E 's/.*[^[:digit:]](([0-9]{1,3}\.){3}[0-9]{1,3})[^[:digit:]].*/\1/p'
# Machen Sie sich die Bedeutung der regex klar!
192.168.0.5

Wenn man für Zeilen, die man für ein Ersetzungsmuster ausschießen will, ein Muster angeben will, so geht dies folgendermaßen:

In [173]:
cat countries.txt
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand

Mehr zu sed finden Sie z.B. auch

Textprozessierungsprache awk

awk ist ein Werkzeug (eigentlich sogar eine Programmiersprache), das sich insbesondere zur Prozessierung von Text in Feldstruktur eignet. Hier beschränken wir uns auf einfache Beispiele.

aus https://www.gnu.org/software/gawk/: "The awk utility interprets a special-purpose programming language that makes it possible to handle simple data-reformatting jobs with just a few lines of code."

Hier ein ganz einfaches Beispiel (als Alternative zu cut):

In [32]:
awk '{print $2 "\t" $3}' logfile-example.log
192.168.0.140	index.html
192.168.0.120	my_test.html
192.168.0.140	login.html
192.168.0.140	bla.html
192.168.0.10	blub.html
	
In [267]:
# erinnerung
cat logfile-example.log # Logfile der Abrufe
# Zeitstempel  IP-des-Web-Servers  Abgerufene-Seite
echo
cat rechnernamen.txt # Ip-addresse Webservername
1618391758	192.168.0.140	index.html
1618391759	192.168.0.120	my_test.html
1618391762	192.168.0.140	login.html
1618391765	192.168.0.140	bla.html
1618391771	192.168.0.10	blub.html

192.168.0.120	sokrates
192.168.0.120	aristoteles
192.168.0.10	platon

Beispiel: Web-Traffic auf die einzelnen Rechner.

Wir möchten die Uhrzeit zusammen mit dem Namen des Webservers.

Mit gnu-awk gawk ist eine Funktion strftime zur Konvertierung von Timestamps zu Datumsangaben verfügbar:

Hinweis: Muss unter Ubuntu/Debian nachinstalliert werden sudo apt-get install gawk, da standardmäßig mawk installiert ist.

In [271]:
join -1 2 -2 1 -t $'\t' <(sort -t $'\t' -k2 logfile-example.log) rechnernamen.txt | \
sort -t $'\t' -k2 | awk '{ print strftime("%m/%d/%Y %H:%M:%S", $2) "\t" $4 }'
04/14/2021 11:15:58	platon
04/14/2021 11:15:59	aristoteles
04/14/2021 11:16:02	platon
04/14/2021 11:16:05	platon
04/14/2021 11:16:11	sokrates

Mehr zu awk finden Sie auch im Online-Buch Shell-Programmierung von Jürgen Wolf.

Übungsaufgaben

Aufgabe

Schreiben Sie einen Einzeiler, die gesamte Anzahl der Duplikate zählt und testen Sie dies an duplicate_countries (siehe oben: cat countries.txt countries_1.txt countries.txt <(echo France) > duplicate_countries)
D.h. für jede Zeile, die mehrfach vorkommt, soll das mehrfache Vorkommen gezählt werden, z.B.

  • kommt Austria einmal vor. Somit ist die Anzahl der Duplikate für Austria Null.
  • kommt Canada zweimal vor. Somit ist die Anzahl der Duplikate für Canada Eins.
  • kommt France dreimal vor. Somit ist die Anzahl der Duplikate für France Zwei.

Das Ergebnis ist für die Datei duplicate_countries somit 7.

In [364]:
cat duplicate_countries
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand
Germany
Austria
Poland
France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand
France

Textvergleiche

comm

comm vergleicht zwei alphabetisch sortiere Dateien:

In [34]:
comm <(seq 1 4) <(seq 2 5)
1
		2
		3
		4
	5

In der ersten Ausgabespalte werden die Zeilen, die nur in Datei 1 vorkommen, dargestellt. in der zweiten Spalte werden die Zeilen angezeigt, die nur in Datei 2 vorkommen. In der dritten Spalte werden die Zeilen dargestellt, die in beiden Dateien vorkommen.

Mit de Schalter -2 kann bespielsweise die Darstellung der zweiten Spate unterdrückt werden. Probieren Sie es aus.

In [14]:
comm -2 <(seq 1 4) <(seq 2 5)
1
	2
	3
	4

Aufgabe

Wie können (mit einem Einzeiler) nur die Länder angezeigt werden, die in den Dateien countries.txt und countries_2.txt (siehe unten) vorkommen?

diff

diff berechnet den Unterschied (differences) (zeilenweise) zwischen zwei Dateien und zeigt diesen an.

Die einfachste Benutzung ist diff datei1 datei2:

In [369]:
echo "Canada
Burkina Faso
France
Democratic Republic of the Kongo
Austria
New Zealand
Germany
Switzerland" > countries_2.txt
In [371]:
diff countries.txt countries_2.txt
1d0
< France
4,5c3,5
< Democratic Republic of the Congo
< Russia
---
> France
> Democratic Republic of the Kongo
> Austria
6a7,8
> Germany
> Switzerland

diff liefert also eine kompake Darstellung der Unterschiede. Dabei werden Operationen angegeben, die datei1 in datei2 überführen.

Änderung Beschreibung
l1dl2 Lösche (delete) die Zeilen ab Zeile l1 in der ersten Datei, die auftauchen würden bei Zeile l2 der zweiten Datei
l1cl2 Ersetze (bzw. ändere, change) die Zeilen l1 der ersten Datei mit den Zeilen l2 der zweiten Datei.
l1al2 Füge (add) die Zeilen ab Position l1 in der ersten Datei hinzu, die auftauchen bei den Zeilen l2 der zweiten Datei.

In unserem Beispiel bedeuten die Differenzangaben:

  • 1d0 Lösche die erste Zeile von datei1. Diese wäre in der 0. Zeile in datei2.
  • 6a7,8 Füge die Zeilen 7 und 8 von datei2 in datei1 ab Zeile 6 hinzu.
  • 4,5c3,5 Ersetze die Zeilen 4 und 5 der ersten Datei mit den Zeile 3 bis 5 der zweiten Datei.

Neben diesen POSIX-konformen Änderungsangaben (Standard bei diff) gibt es noch weitere Formate, wie

  • Context Format - Schalter -c
  • Unified Format - Schalter -u

Diese beeinhalten Kontext, d.h. die Diffenzangaben sind nicht mehr so sensitiv bzgl. Zeilennummerveränderungen.

Context Format

In [372]:
diff -c countries.txt countries_2.txt
*** countries.txt	2021-11-17 09:51:55.432388497 +0100
--- countries_2.txt	2021-11-17 10:29:56.299350472 +0100
***************
*** 1,6 ****
- France
  Canada
  Burkina Faso
! Democratic Republic of the Congo
! Russia
  New Zealand
--- 1,8 ----
  Canada
  Burkina Faso
! France
! Democratic Republic of the Kongo
! Austria
  New Zealand
+ Germany
+ Switzerland

Die Ausgabe beginnt mit den Namen der Dateien und einem Zeitstempel.

  • die erste Datei wird gekennzeichnet mit drei Asterix ***.
  • die zweite Datei wird gekennzeichnet mit drei Strichen---.

Dann werden jeweils die beiden Dateien ausgegeben, hier steht z.B. --- 1,8 ---- für zweite Datei (---) mit Zeilen 1 bis 8. Die Änderungsangaben zur Überführung von Datei 1 in Datei 2 sind:

  • - Zeile löschen (kommt nur bei Datei 1 vor)
  • + Zeie hinzufügen (kommt nur bei Datei 2 vor)
  • Keine Änderung
  • ! Zeile muss geändert werden. Die jeweiligen Versionen sethen bei den jeweiligen Dateien.

Unified Format

In [373]:
diff -u countries.txt countries_2.txt
--- countries.txt	2021-11-17 09:51:55.432388497 +0100
+++ countries_2.txt	2021-11-17 10:29:56.299350472 +0100
@@ -1,6 +1,8 @@
-France
 Canada
 Burkina Faso
-Democratic Republic of the Congo
-Russia
+France
+Democratic Republic of the Kongo
+Austria
 New Zealand
+Germany
+Switzerland

Das Unified Format ähnelt dem *Context Format* bezüglich der ersten drei Zeilen. Die Bedeutungen der Zeichen-,+und ` ist auch die Gleiche, wie im Context Format. Am einfachsten sind die Angaben ab Zeile 4 zu verstehen, wenn man die Angaben zeilenweise zusammen mit Datei 1 liest und die Änderungen bzw. Nicht-Änderung anwendet, sodass man Datei 2 erhält.

patch

Die Änderungsdatei die diff erzeugt, kann automatsch mittels patch auf eine Datei angewendet werden. So kann man Änderungen einfach nachvollziehen. Wenn außerdem in einer (sehr) großen Datei nur eine kleine Änderung vorgenommen wurde, ist es vorteilhaft nur die Änderung (z.B. über ein Netzwerk) zu übertragen, da diese viel kleiner sind.

Dies wird z.B. auch bei der Entwicklung von Software, wie dem Linux-Kernel genutzt. Entwickler erzeugen relative kleine Code-Änderungen auf dem Kernel Source Tree (Dateibaum, kann rekursiv mit diff durchlaufen werden). Diese werden als diff-Dateien weitergegeben.

  • Code-Reviewer sehen genau, wo welche Änderungen vorgenommen wurden.
  • Es ist einfach die kleinen diff-Dateien auszutauschen, als den kompletten Source Tree.

Dieser Prozess bei der verteilten Softwareentwicklung wird mit Versionkontrollsystemen, wie git, durchgeführt, die unter der Haube diff und patch verwenden.

Hier zwei weitere Versionen der Datei, um zu zeigen, dass patch auf Dateien mit kleinen Änderungen der Ursprungsdatei (außerhalb des Kontextes) angewendet werden kann.

In [51]:
echo "Kanada
Burkina Faso
France
Democratic Republic of the Kongo
Austria
New Zealand
Germany
Schweiz" > countries_3.txt

echo "Kanada
Burkina Faso
France
Democratic Republic of the Congo
Austria
New Zealand
Germany
Schweiz" > countries_4.txt

In der diff-Datei stehen die Änderungen, die von countries_3.txt zu countries_2.txt führen:

In [55]:
# Reduktion des Contexs auf 2 (default 3)
diff --context=2 countries_3.txt countries_2.txt | tee countries.patch
*** countries_3.txt	2021-04-15 15:30:09.854399409 +0200
--- countries_2.txt	2021-04-15 15:25:38.919496270 +0200
***************
*** 1,3 ****
! Kanada
  Burkina Faso
  France
--- 1,3 ----
! Canada
  Burkina Faso
  France
***************
*** 6,8 ****
  New Zealand
  Germany
! Schweiz
--- 6,8 ----
  New Zealand
  Germany
! Switzerland

Aber wir patchen countries_4.txt. Hier ist die Schreibweise von Congo auf C korrigiert. Dies berücksichtigt der Patch (Patch-Datei) nicht.

In [60]:
# patch the counties.txt file (which file to patch is in the patch file)
#patch < countries.patch

# but we want path countries_3, so we give this filename
# -i Read  the  patch from patchfile.  If patchfile is -, read from standard input, the default.
# -b for backup would copy the original countries_3.txt to countries_3.txt.orig
patch -b -i countries.patch countries_4.txt
patching file countries_4.txt
In [61]:
ls countries_4.txt*
countries_4.txt  countries_4.txt.orig  countries_4.txt.rej
In [57]:
cat countries_4.txt
Canada
Burkina Faso
France
Democratic Republic of the Congo
Austria
New Zealand
Germany
Switzerland
In [58]:
# -R Reverses the previous patch
patch -p0 -R -i countries.patch countries_4.txt
patching file countries_4.txt
In [59]:
cat countries_4.txt
Kanada
Burkina Faso
France
Democratic Republic of the Congo
Austria
New Zealand
Germany
Schweiz

Aufgabe

Was passiert im obigen patch-Beispiel, wenn beim diff die Standardgröße des Kontext verwendet wird. Erklären Sie dies.

Übungen

Aufgabe

Zählen Sie die Anzahl der unterschiedlichen Worte der Datei greetings mit einem Einzeiler. Dabei sollen Worte die groß- oder kleingeschrieben werden, nur einmal zählten. Also alle Wörter Hallo bzw. hallo zählen nur einmal. Insgesamt gibt es 6 verschiedliche Terme (normalisierte Worte) in greetings.

In [ ]:
echo "Hallo Welt
hallo welt sind Grüße
an die Welt" > greetings
Aufgabe

Wie oft kommt das Wort "Technik" auf der www-Startseite der HTW-Berlin vor?

  • Nutzen Sie curl --silent https://www.htw-berlin.de, um den Inhalt der Seite herunterzuladen und pipen Sie den Inhalt direkt weiter in eine Befehlskette.
  • Nutzen Sie grep zur Filterung.

Aufgabe

Analysieren Sie die untenstehenden Beispiele.

  • Experimentieren Sie mit Teilen der Kette und lesen Sie die Manpages der Kommando.
In [272]:
man ls | sed -e 's/[^[:alpha:]]/ /g' | tr '\n' " " |  tr -s " " | tr " " '\n'| \
tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl
     1	     20 sort
     2	     20 of
     3	     19 by
     4	     17 with
     5	     17 the
     6	     16 to
     7	     16 list
     8	     15 file
     9	     15 and
    10	     14 time
    11	     14 l
    12	     13 style
    13	     13 is
    14	     12 or
    15	     12 not
    16	     12 ls
    17	     12 a
    18	     10 size
    19	     10 format
    20	     10 entries
    21	     10 do
    22	      9 word
    23	      9 show
    24	      9 print
    25	      9 if
    26	      8 g
    27	      8 for
    28	      8 default
    29	      7 use
    30	      6 when
    31	      6 shell
    32	      6 output
    33	      6 names
    34	      6 m
    35	      6 line
    36	      6 in
    37	      6 group
    38	      6 gnu
    39	      6 first
    40	      6 each
    41	      6 command
    42	      6 color
    43	      6 c
    44	      6 access
    45	      5 x
    46	      5 version
    47	      5 u
    48	      5 t
    49	      5 name
    50	      5 long
    51	      5 like
    52	      5 instead
    53	      5 information
    54	      5 indicator
    55	      5 entry
    56	      5 e
    57	      5 directory
    58	      5 coreutils
    59	      5 but
    60	      4 software
    61	      4 s
    62	      4 pattern
    63	      4 org
    64	      4 none
    65	      4 newest
    66	      4 link
    67	      4 k
    68	      4 iso
    69	      4 implied
    70	      4 http
    71	      4 full
    72	      4 files
    73	      4 escape
    74	      4 directories
    75	      4 ctime
    76	      4 cols
    77	      4 can
    78	      4 append
    79	      4 always
    80	      3 width
    81	      3 type
    82	      3 symbolic
    83	      3 status
    84	      3 sizes
    85	      3 quoting
    86	      3 q
    87	      3 powers
    88	      3 p
    89	      3 nongraphic
    90	      3 no
    91	      3 never
    92	      3 n
    93	      3 modification
    94	      3 locale
    95	      3 ignore
    96	      3 help
    97	      3 free
    98	      3 f
    99	      3 exit
   100	      3 dereference
   101	      3 characters
   102	      3 be
   103	      3 b
   104	      3 auto
   105	      3 author
   106	      3 as
   107	      3 are
   108	      2 z
   109	      2 www
   110	      2 v
   111	      2 using
   112	      2 user
   113	      2 units
   114	      2 this
   115	      2 terminal
   116	      2 slash
   117	      2 set
   118	      2 see
   119	      2 reverse
   120	      2 recent
   121	      2 readable
   122	      2 r
   123	      2 posix
   124	      2 otherwise
   125	      2 order
   126	      2 options
   127	      2 option
   128	      2 only
   129	      2 one
   130	      2 omitted
   131	      2 numeric
   132	      2 matching
   133	      2 mandatory
   134	      2 lt
   135	      2 literal
   136	      2 listing
   137	      2 likewise
   138	      2 it
   139	      2 info
   140	      2 i
   141	      2 hyperlink
   142	      2 human
   143	      2 hide
   144	      2 h
   145	      2 gpl
   146	      2 follow
   147	      2 extension
   148	      2 dired
   149	      2 d
   150	      2 copyright
   151	      2 control
   152	      2 context
   153	      2 contents
   154	      2 columns
   155	      2 classify
   156	      2 chars
   157	      2 change
   158	      2 cannot
   159	      2 bugs
   160	      2 blocks
   161	      2 block
   162	      2 below
   163	      2 before
   164	      2 at
   165	      2 argument
   166	      2 any
   167	      2 also
   168	      2 alphabetically
   169	      2 all
   170	      1 you
   171	      1 y
   172	      1 written
   173	      1 without
   174	      1 within
   175	      1 while
   176	      1 warranty
   177	      1 w
   178	      1 via
   179	      1 vertical
   180	      1 verbose
   181	      1 variable
   182	      1 usage
   183	      1 unless
   184	      1 unit
   185	      1 uid
   186	      1 types
   187	      1 trouble
   188	      1 translationproject
   189	      1 translation
   190	      1 too
   191	      1 tion
   192	      1 times
   193	      1 there
   194	      1 then
   195	      1 themselves
   196	      1 them
   197	      1 their
   198	      1 that
   199	      1 than
   200	      1 text
   201	      1 team
   202	      1 takes
   203	      1 tabsize
   204	      1 tab
   205	      1 synopsis
   206	      1 symlink
   207	      1 subdirectory
   208	      1 subdirectories
   209	      1 stops
   210	      1 starting
   211	      1 standard
   212	      1 stallman
   213	      1 specified
   214	      1 speci
   215	      1 sorting
   216	      1 single
   217	      1 si
   218	      1 showing
   219	      1 short
   220	      1 settings
   221	      1 serious
   222	      1 separated
   223	      1 security
   224	      1 scale
   225	      1 richard
   226	      1 reporting
   227	      1 report
   228	      1 references
   229	      1 redistribute
   230	      1 recursively
   231	      1 recursive
   232	      1 rather
   233	      1 quotes
   234	      1 quote
   235	      1 program
   236	      1 problems
   237	      1 prints
   238	      1 printing
   239	      1 pre
   240	      1 points
   241	      1 permitted
   242	      1 per
   243	      1 owner
   244	      1 overridden
   245	      1 outside
   246	      1 optional
   247	      1 online
   248	      1 on
   249	      1 ok
   250	      1 o
   251	      1 numbers
   252	      1 number
   253	      1 nor
   254	      1 non
   255	      1 newline
   256	      1 natural
   257	      1 more
   258	      1 mode
   259	      1 minor
   260	      1 means
   261	      1 mb
   262	      1 mackenzie
   263	      1 locally
   264	      1 listed
   265	      1 links
   266	      1 lines
   267	      1 limit
   268	      1 licenses
   269	      1 license
   270	      1 law
   271	      1 later
   272	      1 last
   273	      1 largest
   274	      1 kibibytes
   275	      1 key
   276	      1 kb
   277	      1 january
   278	      1 itself
   279	      1 invocation
   280	      1 interpreted
   281	      1 integer
   282	      1 inode
   283	      1 informa
   284	      1 index
   285	      1 inc
   286	      1 ids
   287	      1 html
   288	      1 horizontal
   289	      1 grouping
   290	      1 gplv
   291	      1 gid
   292	      1 generate
   293	      1 foundation
   294	      1 fixed
   295	      1 fill
   296	      1 fied
   297	      1 extent
   298	      1 except
   299	      1 example
   300	      1 escapes
   301	      1 environment
   302	      1 ending
   303	      1 enclose
   304	      1 enable
   305	      1 emits
   306	      1 emacs
   307	      1 effect
   308	      1 double
   309	      1 don
   310	      1 documentation
   311	      1 distinguish
   312	      1 display
   313	      1 disk
   314	      1 disables
   315	      1 disabled
   316	      1 disable
   317	      1 dircolors
   318	      1 dir
   319	      1 designed
   320	      1 description
   321	      1 david
   322	      1 date
   323	      1 current
   324	      1 connected
   325	      1 commas
   326	      1 commands
   327	      1 comma
   328	      1 column
   329	      1 colors
   330	      1 colorize
   331	      1 codes
   332	      1 cftuvsux
   333	      1 bytes
   334	      1 byte
   335	      1 both
   336	      1 backups
   337	      1 avoid
   338	      1 available
   339	      1 augmented
   340	      1 au
   341	      1 atime
   342	      1 assume
   343	      1 arguments
   344	      1 applies
   345	      1 an
   346	      1 almost
   347	      1 allocated
   348	      1 across
   349	      1 about
In [273]:
man ls | sed -e 's/[^[:alpha:]]/ /g' | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | \
sort | uniq -c | sort -nr | nl | awk ' $2 > 12 && length($3) >= 2 { printf "%3s\t%10s\t%2s\n", $1, $3, $2}'
# $2 > 12: alle Worte, die mehr als 12-mal vorkommen - in zweiten Spalte ($2) steht die Häufigkeit
# length($3) > 1 : Die Wortlänge muss mindesten zwei sein - in der ditten Spalte stehen die Worte
  1	      sort	20
  2	        of	20
  3	        by	19
  4	      with	17
  5	       the	17
  6	        to	16
  7	      list	16
  8	      file	15
  9	       and	15
 10	      time	14
 12	     style	13
 13	        is	13
In [274]:
man ls | sed -e 's/[^[:alpha:]]/ /g' | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | \
sort | uniq -c | sort -nr | nl | awk '{ print $2}'| paste -sd+ | bc -l
997

Aufgabe

Modifizieren Sie das Beispiel von oben, so dass nur eine Wort-ID (erste Spalte) und die Worte in der Ausgabe vorkommen. Die häufigsten Worte sollen dabei die niedrigste ID haben, wie oben, d.h. die Ausgabe soll folgendermaßen beginnen:

1   sort
2   of
3   by
4   with
...

Aufgabe

Erstellen Sie ein bash-Script, das ein Befehlsinhaltsverzeichnis als HTML-Seite (Grundgerüst einer HTML Seite) mit Link-Referenzen zu den entsprechenden Webseiten erstellt. Die mögliche relevanten Seiten sind alle relative Links der Seite http://christianherta.de/lehre/praktischeInformatik/praktischeInformatik.html.

Die erzeugte HTML-Seite soll in etwas wie folgt aussehen:

<!DOCTYPE html>                                                                  
  <!-- from https://wiki.selfhtml.org/wiki/HTML/Tutorials/HTML5/Grundger%C3%BCst -->                                                                               
  <html lang="de">                                                                 
    <head>                                                                         
      <meta charset="utf-8">                                                       
      <meta name="viewport" content="width=device-width, initial-scale=1.0">       
      <title>Befehlsverzeichnis</title>                                                         
    </head>                                                                        
    <body>      
     <a href="http://christianherta.de/lehre/praktischeInformatik/./bash/bash_1.html">cd</a></br>
     <a href="http://christianherta.de/lehre/praktischeInformatik/./bash/bash_1.html">chmod</a></br> 
     ....
     <a href="http://christianherta.de/lehre/praktischeInformatik/./Netzwerke/Netzwerke_1.html">traceroute</a></br> 
    </body>                                                                        
  </html>

Hinweise:

Versuchen Sie nicht eine perfekte Lösung zu finden. Solche Skripte sind oft ein Hack für eine schnelle Lösung. Überlegen Sie sich zur Extraktion der Befehle entsprechende Extraktionspipelines. Dies arbeiten typischerweise mit regulären Ausdrücken. Sehen Sie sich hierzu den HTML-Quellcode der Seiten an. Bei vielen Browser können Sie hierzu die Tasktenkombination Strg Shift c verwenden.

Bei der Extraktionspipeline müssen Sie zudem typischerweise eine Abwägung zwischen Precision und Recall machen:

  • Wie viele falsche "Befehle" wollen Sie in der Liste erlauben. Precision ist dabei der Anteil der richtigen Befehle an allen extrahierten Befehlen.
  • Wieviel Prozent, der auf den Seiten stehenden Befehle, wollen Sie mit der Liste abdecken. Dies ist der Recall.

Überlegen Sie sich außerdem,welche Probleme beim Programmieren auftreten. Insbesondere welche Datenstrukturen die Entwicklung erleichtern würden. Wo liegen die Grenzen von bash-Skripten?