Hier erzeugen wir ein paar Text-Dateien, die wir später brauchten:
echo "France
Canada
Burkina Faso
Democratic Republic of the Congo
Russia
New Zealand" > countries.txt
echo "Germany
Austria
Poland" > countries_1.txt
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
echo -e "192.168.0.10\tsokrates
192.168.0.120\taristoteles
192.168.0.140\tplaton" > rechnernamen.txt
Einstieg in die Bash-Shell - Teil 4
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.
Man benötigt dies in der Praxis beispielsweise für die automatische Änderung von Konfigurationsdateien oder zur Logfile-Analyse.
Hinweis: Viele Log-Dateien finden Sie im Verzeichnis /var/log
. Diese sind teilweise komprimiert und können z.B. mit zcat
zum Standardoutput (stdout) entpackt werden.
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.
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.
# Anzeigen der Beispieldatei (siehe oben)
cat logfile-example.log
# zeitstempel IP Webseite
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
# Hinweis:
# die Zeitstempelangaben können mit `date` für Menschen übersetzt werden:
date -d @1618391759
Do 16. Jun 10:40:56 CEST 2022
# 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:
# 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 (sortiertes) Datenfeld haben, verbinden.
Beispiel: Mit einer Datei mit IP-Rechnername Zuweisung können wir das sortierte log-File (in Process Substitution) mit den Rechnernamen verbinden:
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
# falls unsortiert
join -1 2 -2 1 -t $'\t' logfile-example.log rechnernamen.txt
join: logfile-example.log:2: is not sorted: 1618391759 192.168.0.120 my_test.html 192.168.0.140 1618391758 index.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.
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.:
# Hier gibt es (unterschiedlich) 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
# dadurch werden die falschen Felder ausgewählt (eventuell leere):
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.
# 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 -
crontab
-Datei konfiguriert, mehr siehe z.B. in der [Ubuntu-Cron Dokumentation]. Die crontab
-Datei wird in der regel nicht direkt modifiziert, sondern z.B. über den Befehl crontab -e
.-
bei crontab Kommando steht für die Pseudodatei stdin siehe man crontab
. Dadurch wird die durch die Pipe modifizierte cron-Datei "geschrieben".
(https://wiki.ubuntuusers.de/Cron/) oder in Linux-Praxis: zeitbezogene Kommandos.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.
# 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
sort duplicate_countries | 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/:
# mit Sequenzen wies
seq 1 5
1 2 3 4 5
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
# auch zeilenweise mögich
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:
echo $(paste -sd+ <(seq 1 5))
paste -sd+ <(seq 1 5) | bc
1+2+3+4+5 15
Ersetzen Sie die Zeitstempel von logfile-example.log
durch für Menschen lesbare Zeitangaben.
Gewünschte Ausgabe:
Mi 14. Apr 11:15:58 CEST 2021 192.168.0.140 index.html
Mi 14. Apr 11:15:59 CEST 2021 192.168.0.120 my_test.html
Mi 14. Apr 11:16:02 CEST 2021 192.168.0.140 login.html
Mi 14. Apr 11:16:05 CEST 2021 192.168.0.140 bla.html
Mi 14. Apr 11:16:11 CEST 2021 192.168.0.10 blub.html
Hinweis: Eine mögliche Lösung besteht aus der Kombination von paste
, cut
, date
und Process Substitution.
cat logfile-example.log
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
nl
¶nl
erzeugt eine Nummerierung der Zeilen.
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:
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.
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.
whatis split
#rm xa*
split (1) - split a file into pieces split (1posix) - split files into pieces
split -b 20 countries.txt
ls x* # so heißen die Dateien per default
xaa xab xac xad
# 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 R# @ epublic of the Congo# @ Russia New Zealand # ------------------- France Canada Burkina Faso Democratic Republic of the Congo Russia New Zealand
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# 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
# Beispiel: Schreibe Fehlerausgabe eines Kommandos zusätzlich in eine log-Datei
# Die log-Datei soll das Datum im Namen haben (ohne Leerzeichen!)
# hier mit echo "..." um es sichtbar zu machen
echo "command 2> $(date | sed -E 's/[[:space:]]+/_/g')_command_errors.log"
# aber besser ist als Name
echo "command 2> $(date -Iseconds)_command_errors.log"
command 2> Do_12._Mai_13:50:12_CEST_2022_command_errors.log command 2> 2022-05-12T13:50:12+02:00_command_errors.log
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 (...)
:
cat countries.txt | sed -E 's/(a.a)/a\1a/g'
France Caanaada Burkina Faso Democratic Republic of the Congo Russia New Zeaalaand
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.
var="Das em-Tag markiert in HTML den Text <em>kursiv</em>."
Dazu verwenden wir folgendes Muster:
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:
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:
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.
Hinweis:
perl
erzielt man mit perl
durch $1
,$2
etc.# Beispiel
echo $var | perl -pe 's/.*(<.+>).*(<.+>)./tags sind $1 und $2/'
tags sind <em> und </em>
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.
sed
unterstüzt nicht ein optionales Zeichen über ?
. Hier müssen Sie auf range {0,1}
ausweichen. sed -E /pattern/!d
lassen sich alle Zeilen löschen, die nicht auf das Pattern zutreffen.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/ und so weiter. 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!).
Geben Sie alle Zeilen mit http:\\
bzw https:\\
aus. Ersetzen Sie im Text dabei vor dem ersten "http://" mit Nichts, d.h. folgendes soll ausgegeben werden:
http://wikipedia.de oder http://christianherta.de/ und so weiter.
http://localhost:8080/mein/pfad oder mit einem Usernamen
http://benutzername@www.mydomain.de/seite/beispiel.php für
http://www.mydomain.de/seite//beispiel.php#textmarke
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.
egrep 'http://|https://' text_with_urls.txt | perl -pe 's/^.*?(https?:\/\/)/$1/g'
http://wikipedia.de oder http://christianherta.de/ und so weiter. http://localhost:8080/mein/pfad oder mit einem Usernamen http://benutzername@www.mydomain.de/seite/beispiel.php für http://www.mydomain.de/seite//beispiel.php#textmarke 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.
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:
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:
# Ersetze alle "a" durch "e" außer in den Zeilen mit einem großen "C"
sed -E '/C/! s/a/e/g' countries.txt
Frence Canada Burkine Feso Democratic Republic of the Congo Russie New Zeelend
sed
cheet sheet:
Mehr zu sed
finden Sie z.B. auch
sed
manualawk
¶"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." aus https://www.gnu.org/software/gawk/
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.
Hier ein ganz einfaches Beispiel (als Alternative zu cut
):
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
# 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.10 sokrates 192.168.0.120 aristoteles 192.168.0.140 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.
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
gawk
-User GuideSchreiben 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.
Austria
einmal vor. Somit ist die Anzahl der Duplikate für Austria
Null.Canada
zweimal vor. Somit ist die Anzahl der Duplikate für Canada
Eins.France
dreimal vor. Somit ist die Anzahl der Duplikate für France
Zwei.Das Ergebnis ist für die Datei duplicate_countries
somit 7.
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
comm
¶comm
vergleicht zwei alphabetisch sortiere Dateien:
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.
comm -2 <(seq 1 4) <(seq 2 5)
1 2 3 4
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 (zeilenweise) den Unterschied (differences) zwischen zwei Dateien und zeigt diesen an.
Die einfachste Benutzung ist diff datei1 datei2
:
echo "Canada
Burkina Faso
France
Democratic Republic of the Kongo
Austria
New Zealand
Germany
Switzerland" > countries_2.txt
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
.4,5c3,5
Ersetze die Zeilen 4
und 5
der ersten Datei mit den Zeile 3
bis 5
der zweiten Datei.6a7,8
Füge die Zeilen 7
und 8
von datei2
in datei1
ab Zeile 6
hinzu.Neben diesen POSIX-konformen Änderungsangaben (Standard bei diff
) gibt es noch weitere Formate, wie
-c
-u
Diese beeinhalten Kontext, d.h. die Diffenzangaben sind nicht mehr so sensitiv bzgl. Zeilennummerveränderungen.
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.
***
.---
.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)+
Zeile hinzufügen (kommt nur bei Datei 2 vor)
Keine Änderung!
Zeile muss geändert werden. Die jeweiligen Versionen stehen bei den jeweiligen Dateien.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.
Bemerkung: Für eine einfach zu lesenden Zweispaltenansicht der Unterschiede zweier Text-Dateien gibt es das Kommandozeilentool icdiff.
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.
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.
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
cat countries_2.txt
Canada Burkina Faso France Democratic Republic of the Kongo Austria New Zealand Germany Switzerland
In der diff-Datei stehen die Änderungen, die von countries_3.txt
zu countries_2.txt
führen:
# Reduktion des Kontexts auf 2 (default 3)
diff --context=2 countries_3.txt countries_2.txt | tee countries.patch
*** countries_3.txt 2021-12-06 10:13:36.303466356 +0100 --- countries_2.txt 2021-12-06 09:44:25.445222422 +0100 *************** *** 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.
# 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
ls countries_4.txt*
countries_4.txt countries_4.txt.orig countries_4.txt.rej
cat countries_4.txt
Canada Burkina Faso France Democratic Republic of the Congo Austria New Zealand Germany Switzerland
# -R Reverses the previous patch
patch -p0 -R -i countries.patch countries_4.txt
patching file countries_4.txt
cat countries_4.txt
Kanada Burkina Faso France Democratic Republic of the Congo Austria New Zealand Germany Schweiz
Was passiert im obigen patch
-Beispiel, wenn beim diff
die Standardgröße des Kontext verwendet wird. Erklären Sie dies.
Gängige moderne Datenaustauschformate sind meist Text-basiert, d.h. es sind Textdateien in einem speziellen Format. Beispiele sind hier JSON, YAML und XML.
Für diese gängigen textbasierten Datenaustauschformate gibt es auch unterschiedliche Kommandozeilenwerkzeuge, wie z.B.
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
.
echo "Hallo Welt
hallo welt sind Grüße
an die Welt" > greetings
Wie oft kommt das Wort "Technik" auf der www-Startseite der HTW-Berlin vor?
curl --silent https://www.htw-berlin.de
, um den Inhalt der Seite herunterzuladen und pipen Sie den Inhalt direkt weiter in eine Befehlskette. grep
zur Filterung.Analysieren Sie die untenstehenden Beispiele.
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
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
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
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
...
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:
Ü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?