650 lines
27 KiB
TeX
650 lines
27 KiB
TeX
|
% <#>---------------------------------------------------------------------------<#>
|
||
|
\section{Text Processing}%
|
||
|
\label{sec:text-processing}
|
||
|
|
||
|
Unter Text Processing versteht man das Einlesen, Filtern, Modifizieren und
|
||
|
Umleiten von Dateien oder Streams. Dieses Kapitel behandelt daher die gängigen
|
||
|
Programme, welche ein Linux Systemadministrator oder Entwickler als sein
|
||
|
Handwerkszeug betrachten würde.
|
||
|
|
||
|
Bevor jedoch die Programme erklärt werden, sind die Grundlagen von Umleitungen
|
||
|
und Befehlsverkettungen notwendig.
|
||
|
|
||
|
% <#>---------------------------------------------------------------------------<#>
|
||
|
\subsection{Umleitungen und Befehlsverkettungen}
|
||
|
\label{sec:text-processing.redirections}
|
||
|
|
||
|
Nachfolgend wird der Begriff Befehlsverkettung oder auch Kommandoverkettung
|
||
|
anhand von Syntax Beispielen näher erläutert.
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \bashinline{cmd1 | cmd2} \\
|
||
|
Die \textit{Pipe} verbindet die Standard-Ausgabe (\textit{stdout}) eines
|
||
|
Programms mit der Eingabe (\textit{stdin}) eines anderen Programms.
|
||
|
\item \bashinline{cmd1 ; cmd2} \\
|
||
|
Führt erst das Programm \textit{cmd1} aus und anschließend das Programm
|
||
|
\textit{cmd2} - ganz egal, ob Programm \textit{cmd1} einen Fehler geworfen hat
|
||
|
oder nicht.
|
||
|
\item \bashinline{cmd1 && cmd2} \\
|
||
|
Jedes Programm übergibt zuletzt an das Betriebssystem einen sogenannten
|
||
|
Returncode (auch Errorlevel oder Exitlevel genannt). Bei einem Returncode 0
|
||
|
ist alles in Ordnung, bei einem Wert ungleich 0 trat ein Fehler auf. In diesem
|
||
|
Fall der Kombination wird \textit{cmd2} nur ausgeführt, wenn der Returncode
|
||
|
von \textit{cmd1} gleich 0 war.
|
||
|
\item \bashinline{cmd1 || cmd2} \\
|
||
|
Dies ist genau das Gegenteil. Das Programm \textit{cmd2} wird nur ausgeführt,
|
||
|
wenn der Returncode von \textit{cmd1} ungleich 0 war.
|
||
|
\item \bashinline{cmd1 & cmd2} \\
|
||
|
Das \&-Zeichen am Ende einer Befehlszeile veranlasst, dass das Programm in den
|
||
|
Hintergrund verlagert wird und ihnen die Prompt wieder zur Verfügung gestellt
|
||
|
wird. Dadurch ist es möglich über eine Session mehrere Befehle auszuführen.
|
||
|
Nichts anderes passiert hier. Das Programm \textit{cmd1} wird in den
|
||
|
Hintergrund verlagert. Das Programm \textit{cmd2} wird nach der Verlagerung
|
||
|
ausgeführt.
|
||
|
\end{itemize}
|
||
|
|
||
|
Neben den Befehlsverkettungen gibt es auch noch Umleitungen der Ein- und
|
||
|
Ausgabekanäle (\textit{stdin}, \textit{stdout}, \textit{stderr}).
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \bashinline{cmd > /tmp/output.log} oder auch \bashinline{cmd 1> /tmp/output.log} \\
|
||
|
Die Standard-Ausgabe \textit{stdout} wird in die Datei \textit{/tmp/output.log}
|
||
|
geschrieben.
|
||
|
\item \bashinline{cmd >> /tmp/output.log} \\
|
||
|
Die Standard-Ausgabe \textit{stdout} wird an die Datei \textit{/tmp/output.log}
|
||
|
angehängt.
|
||
|
\item \bashinline{cmd 2> /tmp/output.log} \\
|
||
|
Die Standard-Fehlerausgabe \textit{stderr} wird an die Datei \textit{/tmp/error.log}
|
||
|
geschrieben.
|
||
|
\item \bashinline{cmd &> /tmp/complete.log} \\
|
||
|
Die Standard-Ausgabe \textit{stdout} und die -Fehlerausgabe \textit{stderr}
|
||
|
werden in die Datei \textit{/tmp/complete.log} geschrieben. geschrieben.
|
||
|
\item \bashinline{cmd < /tmp/input} \\
|
||
|
Die Standard-Eingabe \textit{stdin} erfolgt nicht durch die Tastatur, sondern
|
||
|
aus der Datei \textit{/tmp/input}.
|
||
|
\item \bashinline{cmd << EOT} \\
|
||
|
Die Standard-Eingabe wird nur bis zu der frei wählbaren Zeichenfolge EOT
|
||
|
gelesen und anschließend beendet. Man nennt diese Konstruktion HERE-Dokument.
|
||
|
Dazu später mehr.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{cat}
|
||
|
\label{sec:text-processing.cat}
|
||
|
|
||
|
Das Programm \textit{cat} konkateniert mehrere Dateien und schreibt diese auf
|
||
|
die Standard-Ausgabe \textit{stdout}. Nachfolgend einige Beispiele.
|
||
|
|
||
|
Die Ausgabe der Datei \textit{/etc/passwd}. Die Zeilen wurden gekürzt.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ cat /etc/passwd
|
||
|
root:x:0:0:root:/root:/bin/bash
|
||
|
bin:x:1:1:bin:/bin:/sbin/nologin
|
||
|
daemon:x:2:2:daemon:/sbin:/sbin/nologin
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
Die Datei \textit{/etc/passwd} und \textit{/etc/group} werden zusammengeführt
|
||
|
und nach \textit{/tmp/user-and-groups} umgeleitet.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ cat /etc/passwd /etc/group > /tmp/user-and-groups
|
||
|
\end{bashcode}
|
||
|
|
||
|
In dem folgenden Beispiel wird ein HERE-Dokument genutzt, um die Eingabe in eine
|
||
|
Datei umzuleiten.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ cat > /tmp/my-here-document <<EOF
|
||
|
Hello,
|
||
|
this is my first here document.
|
||
|
EOF
|
||
|
\end{bashcode}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{wc}
|
||
|
\label{sec:text-processing.wc}
|
||
|
|
||
|
Mit Programm \textit{wc} lassen sich die Wörter, Zeilen oder Buchstaben von
|
||
|
einer Standard-Eingabe oder Dateien zählen. Das folgende Beispiel addiert die
|
||
|
Zeilen der Datei \textit{/etc/passwd} und \textit{/etc/group}.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ wc --lines /etc/passwd /etc/group
|
||
|
42 /etc/passwd
|
||
|
62 /etc/group
|
||
|
104 total
|
||
|
\end{bashcode}
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 1a:} Zählen Sie die Buchstaben und Wörter der Datei
|
||
|
\textit{/etc/services}. Definieren Sie die Quelle einmal per Pfad zur Datei und
|
||
|
ein anderes mal per Befehlsverkettung - \textit{Pipe}.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{date}
|
||
|
\label{sec:text-processing.date}
|
||
|
|
||
|
Mit dem Programm \textit{date} können Daten, basierend auf dem aktuellen oder
|
||
|
einem vordefinierten Datum, berechnet werden. Zusätzlich kann das Format anhand
|
||
|
eines Formatstrings bestimmt werden. Die einzelnen Formatattribute sind in der
|
||
|
Dokumentation von \textit{date} beschrieben. Schauen Sie sich für weitere
|
||
|
Informationen die Dokumentation an: \textit{man date}.
|
||
|
|
||
|
Nachfolgend einige Beispiele ein Datum basierend auf den Anforderungen zu
|
||
|
berechnen.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ # Gibt das aktuelle Datum und die Uhrzeit aus
|
||
|
$ date '+%Y-%m-%d %H:%M:%S'
|
||
|
2020-11-14 16:35:07
|
||
|
|
||
|
$ # Gibt das Datum des letzten Montags aus
|
||
|
$ date --date 'last monday' '+%Y-%m-%d'
|
||
|
2020-11-09
|
||
|
|
||
|
$ # Berechnet das Datum des letzten Montags vor zwei Wochen
|
||
|
$ date --date 'last monday + 14 day ago' '+%Y-%m-%d'
|
||
|
2020-10-26
|
||
|
|
||
|
$ # Gibt das Datum des kommenden Montags aus
|
||
|
$ date --date 'next monday' '+%Y-%m-%d'
|
||
|
2020-11-16
|
||
|
|
||
|
$ # Berechnet das Datum des nächsten Montags in zwei Wochen
|
||
|
$ date --date 'next monday + 14 day' '+%Y-%m-%d'
|
||
|
2020-11-30
|
||
|
|
||
|
$ # Berechnet das Datum des letzten Montags in zwei Wochen
|
||
|
$ date --date 'last monday + 14 day' '+%Y-%m-%d'
|
||
|
2020-11-23
|
||
|
|
||
|
$ # Gibt das Datum und die Uhrzeit für die Zeitzone
|
||
|
$ # America/New_York aus
|
||
|
$ TZ=America/New_York date '+%Y-%m-%d %H:%M:%S %z'
|
||
|
2020-11-14 10:41:39 -0500
|
||
|
|
||
|
$ # Gibt das Datum, die Uhrzeit, Kalenderwoche und den Tag aus
|
||
|
$ date '+%A, %Y-%m-%d %H:%M:%S, KW %V'
|
||
|
Samstag, 2020-11-14 16:42:44, KW 46
|
||
|
|
||
|
$ # Addiert zwei Tage auf den 3. Oktober 2020
|
||
|
$ date --date '03 OCT 2020 + 2 day' '+%Y-%m-%d'
|
||
|
2020-10-05
|
||
|
|
||
|
$ # Addiert zwei Tage auf den 3. Oktober vor 5 Jahren.
|
||
|
$ date --date "03 OCT $(date --date '5 year ago' '+%Y') + 2 day" '+%Y-%m-%d'
|
||
|
2015-10-05
|
||
|
\end{bashcode}
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 2a:} Berechnen Sie das Datum des kommenden Sonntags.
|
||
|
Verwenden Sie folgendes Format: \textit{2020-05-31}. Erkundigen Sie sich in
|
||
|
der Dokumentation über weitere Formatattribute.
|
||
|
\item \textbf{Aufgabe 2b:} Berechnen Sie das Datum des letzten Dienstags vor
|
||
|
einem Monat. Verwenden Sie folgendes Format: \textit{Dienstag, 2020-05-31, KW
|
||
|
22}
|
||
|
\item \textbf{Aufgabe 2c:} Berechnen Sie das Datum des Mittwochs der 44 KW vor
|
||
|
12 Jahren für die Zeitzone Africa/Mogadishu. \textit{KW 22, Dienstag, 15
|
||
|
November 2008}.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{sed}
|
||
|
\label{sec:text-processing.sed}
|
||
|
|
||
|
Das Programm \textit{sed} ist ein stream Editor, welcher zum Filtern,
|
||
|
Modifizieren und Löschen von Textstreams geeignet ist.
|
||
|
|
||
|
Das Programm bietet einen sehr großen Umfang an zusätzlichen Optionen an. Nicht
|
||
|
alle Optionen werden behandelt. Der Fokus liegt ausschließlich auf den gängigen
|
||
|
Optionen, welche öfters in der Praxis verwendet werden. Weitere Informationen zu
|
||
|
allen möglichen Optionen können über die Dokumentation abgerufen werden:
|
||
|
\textit{man sed}.
|
||
|
|
||
|
In dem folgenden Beispiel wird der Inhalt der Datei \textit{/etc/passwd} durch
|
||
|
\textit{sed} modifiziert und auf der Standard-Ausgabe ausgegeben. Die
|
||
|
Zeichenkette \textit{root} wird durch \textit{nobody} ersetzt. Vergleichen Sie
|
||
|
die ersten Zeilen der Ausgabe mit der ursprünglichen Datei.
|
||
|
|
||
|
Für komplexere Kriterien können Reguläre Ausdrücke verwendet werden. Falls Ihnen
|
||
|
Reguläre Ausdrücke fremd sind, informieren Sie sich bitte im Internet über
|
||
|
Reguläre Ausdrücke.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ sed 's/root/nobody/g' /etc/passwd
|
||
|
nobody:x:0:0:nobody:/nobody:/bin/bash
|
||
|
bin:x:1:1:bin:/bin:/sbin/nologin
|
||
|
daemon:x:2:2:daemon:/sbin:/sbin/nologin
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
Möchte man Reguläre Ausdrücke verwenden kann das Flag bzw. die Option
|
||
|
\textit{-e, --expression} verwendet werden. Allerdings hat diese Option den
|
||
|
Nachteil, dass Zeichen für Reguläre Ausdrücke maskiert werden müssen. Anders ist
|
||
|
dies bei dem Flag \textit{-E, -r, --regexp-extended}. Die Syntax der Regulären
|
||
|
Ausdrücke ist identisch zu der Syntax von \textit{grep} mit dem Flag
|
||
|
\textit{--perl-regexp} und sollte daher bevorzugt verwendet werden.
|
||
|
|
||
|
In dem folgenden Beispiel wird die Ausgabe umgeleitet in eine Datei.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ sed 's/root/nobody/g' /etc/passwd > /tmp/passwd_modified
|
||
|
\end{bashcode}
|
||
|
|
||
|
Möchte man die selbe Datei anhand von Kriterien Filtern und Modifizieren bietet
|
||
|
sich die Option bzw. das Flag \textit{-i, --in-place} an. Die Änderungen werden
|
||
|
direkt in die Quelldatei übernommen.
|
||
|
|
||
|
In dem folgenden Beispiel wird die Zeile 5 bis 10 und die Zeile 12 aus der Datei
|
||
|
\textit{/tmp/passwd\_modified} entfernt.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ sed --in-place '5,10d;12d' /tmp/passwd_modified
|
||
|
\end{bashcode}
|
||
|
|
||
|
Sicherlich fragen Sie sich nun was die Zeichen \textit{s/}, \textit{d} oder
|
||
|
\textit{/g} zu Beginn oder zum Ende der Bedingung bedeuten. Mit \textit{s/} wird
|
||
|
\textit{sed} mitgeteilt, dass eine Zeichenkette durch eine andere ersetzt werden
|
||
|
soll. Die Zeichen \textit{/g} bedeuten, dass der Ausdruck auf alle Treffer
|
||
|
angewendet werden soll. Eine andere Bedeutung für das \textit{g} ist
|
||
|
\glqq{}global\grqq{}. Nun, wofür \textit{d} steht können Sie sich nun schon
|
||
|
denken - \textit{delete}.
|
||
|
|
||
|
In dem folgenden Beispiel werden alle Zeilen aus der Datei \textit{/etc/passwd}
|
||
|
und \textit{/etc/group} entfernt, die mit der Zeichenkette \textit{root} beginnen.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ sed --regexp-extended '/^root/d' /etc/passwd /etc/group
|
||
|
\end{bashcode}
|
||
|
|
||
|
Ein invert ist ebenfalls möglich. In dem folgenden Beispiel werden alle Zeilen
|
||
|
außer die fünfte Zeile entfernt.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ sed '5!d' /etc/passwd
|
||
|
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
|
||
|
\end{bashcode}
|
||
|
|
||
|
Mithilfe von Gruppen, welche Reguläre Ausdrücke bereitstellen, können komplexe
|
||
|
Zeichenketten gruppiert und neu angeordnet werden. In dem folgenden Beispiel
|
||
|
wird die letzte Spalte der Datei \textit{/etc/passwd} an die erste Stelle
|
||
|
verschoben.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ sed --regexp-extended 's/^(.*):(.*):(.*):(.*):(.*):(.*):(.*)$/\7:\1:\2:\3:\4:\5:\6/g' /etc/passwd
|
||
|
/bin/bash:root:x:0:0:root:/root
|
||
|
/sbin/nologin:bin:x:1:1:bin:/bin
|
||
|
/sbin/nologin:daemon:x:2:2:daemon:/sbin
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 3a:} Löschen Sie alle Zeilen, welche als Shell
|
||
|
\textit{/sbin/nologin} verwenden. Geben Sie die Ausgabe auf der
|
||
|
Standard-Ausgabe aus.
|
||
|
\item \textbf{Aufgabe 3b:} Löschen Sie alle Zeilen aus der Datei
|
||
|
\textit{/etc/passwd} und \textit{/etc/group}, welche mit \textit{root} oder
|
||
|
\textit{system} beginnen. Zählen Sie anschließend die Zeilen. Nutzen Sie dazu
|
||
|
die Umleitung von einem Programm zu einem anderen per \textit{Pipe}.
|
||
|
\item \textbf{Aufgabe 3c:} Bereinigen Sie die Datei \textit{/etc/services}.
|
||
|
Löschen Sie Kommentarzeilen und Zeilen welche leer sind. Manche Zeilen
|
||
|
enthalten am Ende Kommentare, entfernen Sie diese.
|
||
|
\item \textbf{Aufgabe 3d:} Suchen Sie in der Datei \textit{/etc/services} nach
|
||
|
Zeilen, welche mit dem Port 5 beginnen und vertauschen Sie den Port mit dem
|
||
|
Protokoll.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{grep}
|
||
|
\label{sec:text-processing.grep}
|
||
|
|
||
|
Das Programm \textit{grep} wird primär dazu verwendet Dateien oder Streams nach
|
||
|
Schlagwörtern zu filtern. Ähnlich wie bei \textit{sed} können auch hier nicht
|
||
|
alle Optionen oder Flags ausführlich in Detail behandelt werden. Aus diesem
|
||
|
Grund liegt der Fokus erneut auf den gängigen Optionen bzw. Flags aus der Praxis.
|
||
|
|
||
|
Das nachfolgende Beispiel filtert die Datei \textit{/etc/passwd} und
|
||
|
\textit{/etc/group} nach der Zeichenkette \textit{root} und hebt diese farblich
|
||
|
hervor.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ grep --color root /etc/passwd /etc/group
|
||
|
/etc/passwd:root:x:0:0:root:/root:/bin/bash
|
||
|
/etc/passwd:operator:x:11:0:operator:/root:/sbin/nologin
|
||
|
/etc/group:root:x:0:
|
||
|
\end{bashcode}
|
||
|
|
||
|
In dem folgenden Beispiel wird ein Regulärer Ausdruck verwendet, um nach der
|
||
|
Zeichenkette \textit{root} oder \textit{sophie}, beginnend für jede Zeile, in
|
||
|
der Datei \textit{/etc/passwd} zu suchen.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ grep --color --perl-regexp '^(root|sophie)' /etc/passwd
|
||
|
root:x:0:0:root:/root:/bin/bash
|
||
|
sophie:x:2003:100:Sophie Becker:/home/sophie:/bin/bash
|
||
|
\end{bashcode}
|
||
|
|
||
|
Manchmal möchte man allerdings nur die gefundenen Schlagwörter der Zeile
|
||
|
weiterverarbeiten. Dazu stellt das Programm \textit{grep} die Option
|
||
|
\textit{--only-matching} bereit. Nachfolgend wird der gleiche Befehl erneut
|
||
|
ausgeführt mit der Option.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ grep --color --only-matching --perl-regexp '^(root|sophie)' /etc/passwd
|
||
|
root
|
||
|
sophie
|
||
|
\end{bashcode}
|
||
|
|
||
|
Sicherlicht kennen einige die folgende Situation. Unterhalb eines Verzeichnisses
|
||
|
liegen sehr viele Dokumente. Eines von diesen enthält einen Begriff, welcher
|
||
|
dort erklärt oder beschrieben wird. In dem nachfolgenden Beispiel werden alle
|
||
|
Dateien rekursiv geöffnet und nach den Schlagwörtern durchsucht. In der Ausgabe
|
||
|
ist der Dateiname und die Zeile als auch das Schlagwort enthalten, welches den
|
||
|
Treffer ausgelöst hat.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ grep --color --recursive --perl-regexp '(btrfs|subvolume|snapshot)' ~/workspace/linux_ws2021
|
||
|
~/workspace/linux_ws2021/referenzen/bibliothek.bib: @online{fedora33-btrfs-default,
|
||
|
~/workspace/linux_ws2021/referenzen/bibliothek.bib: url = {https://fedoramagazine.org/btrfs-coming-to-fedora-33/},
|
||
|
~/workspace/linux_ws2021/referenzen/bibliothek.bib: @online{was-ist-btrfs,
|
||
|
\end{bashcode}
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 4a:} Suchen Sie alle Zeilen aus der Datei
|
||
|
\textit{/etc/passwd} und \textit{/etc/group} herraus, welche nicht die
|
||
|
Zeichenketten \textit{root} und ihren aktuellen Benutzernamen enthalten.
|
||
|
Nutzen Sie zur Ermittlung ihres Benutzernamens die Umgebungsvariable
|
||
|
\textit{USER}.
|
||
|
\item \textbf{Aufgabe 4b:} Filtern Sie aus der Datei \textit{/etc/passwd} alle
|
||
|
Zeilen, welche mit \textit{system} beginnen. Leiten Sie die Ausgabe weiter an
|
||
|
\textit{sed}, um die Zeichenkette \textit{/sbin/nologin} durch
|
||
|
\textit{/bin/bash} zu ersetzen.
|
||
|
|
||
|
Kleiner Tipp: Statt \textit{/} können auch andere Zeichen, wie
|
||
|
\textit{\#} oder \textit{@}, als Trennung zwischen dem Such- und
|
||
|
Ersetzungspattern verwendet werden. Dies kann hilfreich sein, um ein escapen
|
||
|
von Verzeichnispfaden zu vermeiden.
|
||
|
\item \textbf{Aufgabe 4c:} Zählen Sie alle Zeilen der Datei
|
||
|
\textit{/etc/services}, welche das Protokoll tcp verweden.
|
||
|
\item \textbf{Aufgabe 4d:} Ermitteln Sie, ob in der Datei
|
||
|
\textit{/etc/services} noch andere Protokolle statt tcp und udp verwendet
|
||
|
werden. Gegen Sie die unbekannten Protokolle auf der Standard-Ausgabe aus.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{uniq}
|
||
|
\label{sec:text-processing.uniq}
|
||
|
|
||
|
Manchmal möchte man Treffer zählen. Beispielsweise wird eine Datei durchsucht
|
||
|
nach einem Schlagwort und man stellt sich die Frage, wie oft das Schlagwort in
|
||
|
der Datei verwendet wurde. Um diese Frage zu beantworten ist unter anderem
|
||
|
\textit{uniq} sehr helfreich, denn es kann Mehrfachvorkommen von Zeichenketten
|
||
|
vermeiden und ggfls. zählen.
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 5a:} Zählen Sie wie oft in der Datei
|
||
|
\textit{/etc/passwd} die Zeichkette \textit{root} verwendet wurde. Eventuell
|
||
|
ist es hilfreich \textit{grep} hinzuzuziehen.
|
||
|
\item \textbf{Aufgabe 5b:} Suchen Sie in der Datei \textit{/etc/services} nach
|
||
|
Diensten die nur einen Port bzw Protokoll benötigen. Nutzen Sie auch hier die
|
||
|
bereits kennengelernten Programme.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{column}
|
||
|
\label{sec:text-processing.column}
|
||
|
|
||
|
Manchmal erhält man Dateien, welche für das menschliche Auge unübersichtlich
|
||
|
formatiert sind. Das Programm \textit{column} kann hier insoweit Abhilfe
|
||
|
schaffen, dass die Eingabe in mehrere Spalten formatiert wird.
|
||
|
|
||
|
In dem folgenden Beispiel wird die Datei \textit{/etc/passwd} eingelesen, die
|
||
|
Spalten anhand des Trenners \textit{:} getrennt und als Tabelle auf der
|
||
|
Standard-Ausgabe ausgegeben.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ column --separator ":" --table /etc/passwd
|
||
|
\end{bashcode}
|
||
|
|
||
|
Es können auch zusätzlich Kopfzeilen hinzugefügt werden. Im nächsten Beispiel
|
||
|
ist dies genau der Fall. Die Kopfzeile wird definiert und mit ausgegeben.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ column --separator ":" \
|
||
|
--table \
|
||
|
--table-column "user,password,id,gid,comment,homedir,shell" /etc/passwd
|
||
|
\end{bashcode}
|
||
|
|
||
|
Wahrscheinlich wird das Programm \textit{column} häufiger verwendet, um Dateien
|
||
|
in json umzuwandeln. In dem folgenden Beispiel wird die Datei in json
|
||
|
umgewandelt.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ column --separator ":" \
|
||
|
--table \
|
||
|
--table-name "passwd" \
|
||
|
--table-column "user,password,id,gid,comment,homedir,shell" \
|
||
|
--json /etc/passwd
|
||
|
\end{bashcode}
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 6a:} Filtern Sie alle Zeilen aus der Datei
|
||
|
\textit{/etc/services}, welches das UDP-Protokoll verwenden und mit dem
|
||
|
Buchstaben \textit{n} beginnen. Teilen Sie die zweite Spalte auf in Port und
|
||
|
Protokoll. Die Ausgabe muss in ein json umgewandelt werden. Der Tabellenname
|
||
|
ist \textit{services} und die Spalten sollen \textit{service}, \textit{port}
|
||
|
und \textit{protocol} heißen. Speichern Sie die Ausgabe in die Datei
|
||
|
\textit{/tmp/services.json} ab.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{head und tail}
|
||
|
\label{sec:text-processing.head-und-tail}
|
||
|
|
||
|
Mit den bisherigen Programmen können wir schon einiges erreichen, doch fehlt es
|
||
|
an der Möglichkeiten die Ausgabe zu beschränken. Mit den Programmen
|
||
|
\textit{head} und \textit{tail} kann genau das erreicht werden. Beide Programme
|
||
|
schreiben auf die Standard-Ausgabe \textit{stdout}, wenn keine Umleitung
|
||
|
definiert ist.
|
||
|
|
||
|
Mit dem Programm \textit{head} können n Zeilen, beginnend ab der ersten Zeile,
|
||
|
ausgegeben werden. Das Programm \textit{tail} dagegen zählt ab der letzten
|
||
|
Zeile.
|
||
|
|
||
|
In dem nachfolgenden Beispiel werden die ersten drei Zeilen der Datei
|
||
|
\textit{/etc/passwd} ausgegeben.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ head --lines=3 /etc/passwd
|
||
|
root:x:0:0:root:/root:/bin/bash
|
||
|
bin:x:1:1:bin:/bin:/sbin/nologin
|
||
|
daemon:x:2:2:daemon:/sbin:/sbin/nologin
|
||
|
\end{bashcode}
|
||
|
|
||
|
Das nächste Beispiel gibt die letzten drei Zeilen aus.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ tail --lines=3 /etc/passwd
|
||
|
tobias:x:2005:100:Tobias Moretti:/home/tobias:/bin/bash
|
||
|
lisa:x:2006:100:Lisa Meerkamp:/home/lisa:/bin/bash
|
||
|
manfred:x:2007:100:Manfred Krupp:/home/manfred:/bin/bash
|
||
|
\end{bashcode}
|
||
|
|
||
|
Zusätzlich besitzt \textit{tail} die Möglichkeit alle Zeilen, beginnend ab der
|
||
|
n-ten Zeile, auszugeben. In dem folgenden Beispiel wird die Datei
|
||
|
\textit{/etc/passwd} ab der 35en Zeile ausgegeben. Die Ausgabe ist zusätzlich
|
||
|
noch einmal gekürzt.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ tail --lines=+35 /etc/passwd
|
||
|
hugo:x:2000:100:Hugo McKinnock:/home/hugo:/bin/bash
|
||
|
hans:x:2001:100:Hans Rakinzsky:/home/hans:/bin/bash
|
||
|
marie:x:2002:100:Marie Haseloff:/home/marie:/bin/bash
|
||
|
sophie:x:2003:100:Sophie Becker:/home/sophie:/bin/bash
|
||
|
\end{bashcode}
|
||
|
|
||
|
Manche Programme, insbesondere Dienste, schreibe Log-Dateien, welche gerade für
|
||
|
Administratoren oder Entwickler interessant sein können. Um ausschließlich die
|
||
|
neusten Zeilen aus einer Logdatei zu erhalten bietet das Programm \textit{tail}
|
||
|
die Option \textit{-f, --follow} an. Dadurch wird veranlasst, dass stets neu
|
||
|
hinzukommende Zeilen direkt ausgegeben werden, ohne das das Programm erneut
|
||
|
gestartet werden muss.
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{cut}
|
||
|
\label{sec:text-processing.cut}
|
||
|
|
||
|
Das Programm \textit{cut} ist genau das richtige Programm, um Spalten, getrennt
|
||
|
durch einen Trenner, auszuwählen. In dem nachfolgenden Beispiel wird
|
||
|
ausschließlich die erste Zeile der Datei \textit{/etc/passwd} ausgegeben.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ cut --delimiter=":" --fields=1 /etc/passwd
|
||
|
root
|
||
|
bin
|
||
|
daemon
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
Es ist auch möglich mehrere Spalten zu selektieren. In dem folgenden Beispiel
|
||
|
wird ausschließlich die zweite bis vierte und siebte Spalte selektiert.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ cut --delimiter=":" --fields=2-4,7 /etc/passwd
|
||
|
x:0:0:/bin/bash
|
||
|
x:1:1:/sbin/nologin
|
||
|
x:2:2:/sbin/nologin
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 7a:} Nutzen Sie die bereits bekannten Programme, um die
|
||
|
Datei \textit{/etc/services} zu bereinigen. Lassen Sie sich anschließend nur
|
||
|
die Spalte mit den Ports und Protokollen anzeigen. Leiten Sie die Ausgabe in
|
||
|
die Datei \textit{/tmp/port-protocol} um.
|
||
|
\item \textbf{Aufgabe 7b:} Verwenden Sie die Programme \textit{echo},
|
||
|
\textit{cut}, \textit{cat}, \textit{tail} und \textit{head}, um die folgenden
|
||
|
Felder aus der Datei \text{/etc/passwd} zu selektieren. Spalte/Zeile: (1/1),
|
||
|
(3/7), (5/3), (7/4).
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{sort}
|
||
|
\label{sec:text-processing.sort}
|
||
|
|
||
|
Um nach Spalten zu sortieren wird durch die GNU Core Utility Collection das
|
||
|
Programm \textit{sort} bereit gestellt. Dieses Programm kann nach mehreren
|
||
|
möglichen Optionen sortieren. Nach einfachem Text, nach Speichergrößen, Zahlen,
|
||
|
Monaten (JAN$<$DEC) oder Versionen. Weitere Optionen sind in der Dokumentation
|
||
|
beschrieben.
|
||
|
|
||
|
Das nächste Beispiel behandelt die Sortierung der Ausgabe des Programms
|
||
|
\textit{df}. Das Programm \textit{df} gibt Informationen über den gesamten,
|
||
|
freien und verbrauchten Speicherplatz der eingehängten Partitionen aus. Die
|
||
|
Ausgabe wird nach der dritten Spalte aufsteigend sortiert. Dazu wird die Option
|
||
|
bzw. das Flag \textit{-k, --key} übergeben. Durch dieses Flag kann die Spalte
|
||
|
zur Sortierung übergeben werden. Mittels tail wird zudem die Kopfzeile
|
||
|
abgeschnitten.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ df | tail --lines +2 | sort --key 3 --numeric-sort
|
||
|
tmpfs 401876 0 401876 0% /run/user/0
|
||
|
tmpfs 2009384 4 2009380 1% /tmp
|
||
|
tmpfs 2009384 1068 2008316 1% /run
|
||
|
/dev/vda1 613184 8740 604444 2% /boot/efi
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
Die Speichergrößen können auch abgerundet werden durch die gängigen
|
||
|
Speicherabkürzungen K (Kilobyte), M (Megabyte), G (Gigabyte), T (Terabyte) ect.
|
||
|
In dem folgenden Beispiel wird dazu die Option \textit{--human-readable} dem
|
||
|
Programm \textit{df} übergeben. Anschließend muss sort mitgeteilt werden, dass
|
||
|
die Spalte nun Speichergrößen mit ihren Abkürzungen enthält. Andernfalls
|
||
|
sortiert \textit{sort} falsch.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ df --human-readable | tail --lines +2 | sort --key 3 --human-numeric-sort
|
||
|
tmpfs 393M 0 393M 0% /run/user/0
|
||
|
tmpfs 2,0G 4,0K 2,0G 1% /tmp
|
||
|
tmpfs 2,0G 1,1M 2,0G 1% /run
|
||
|
/dev/vda1 599M 8,6M 591M 2% /boot/efi
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
In den beiden Beispielen wurde stets nach einer Spalte sortiert. Es ist jedoch
|
||
|
auch möglich eine Sortierreihenfolge zu definieren. Dies wird erreicht indem das
|
||
|
Flag \textit{-k, --key} mehrfach definiert wird. In dem folgenden Beispiel wird
|
||
|
erst nach dem Mountpoint sortiert und anschließend nach dem gesamten
|
||
|
Speicherplatz.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ df --human-readable | tail --lines +2 | sort --key 6 --key 3h
|
||
|
/dev/vda4 17G 4,0G 13G 25% /
|
||
|
/dev/vda2 1014M 228M 787M 23% /boot
|
||
|
/dev/vda1 599M 8,6M 591M 2% /boot/efi
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
Sicherlich fragen Sie sich nun, was der Buchstabe \textit{h} bei Angabe der
|
||
|
zweiten Spalte bedeutet. Es ist möglich \textit{sort} mitzuteilen welche Spalte
|
||
|
welches Format besitzt. Der Buchstabe \textit{h} bedeutet nichts weiteres als
|
||
|
\textit{human-numeric-sort}. Informieren Sie sich in der Dokumentation über die
|
||
|
anderen Buchstaben, um einer Spalte ein Schema zuzuweisen.
|
||
|
|
||
|
In dem folgenden Beispiel wird nach dem dritten Zeichen, der ersten Spalte
|
||
|
von der Ausgabe \textit{df}, absteigend sortiert.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ df | tail --lines +2 | sort --reverse --key 1.3
|
||
|
devtmpfs 1989716 0 1989716 0% /dev
|
||
|
tmpfs 401876 0 401876 0% /run/user/0
|
||
|
tmpfs 2009384 4 2009380 1% /tmp
|
||
|
...
|
||
|
\end{bashcode}
|
||
|
|
||
|
\begin{itemize}[label={},itemsep=0pt]
|
||
|
\item \textbf{Aufgabe 8a:} Bereinigen Sie die Datei \textit{/etc/services} und
|
||
|
leiten die Ausgabe an \textit{sort} weiter. Sortieren Sie aufsteigend nach dem
|
||
|
Port, absteigend nach dem Protokoll und anschließend aufsteigend nach dem
|
||
|
Namen.
|
||
|
\item \textbf{Aufgabe 8b:} Sortieren Sie die Datei \textit{/etc/passwd} nach
|
||
|
der \textit{UID} und \textit{GID} absteigend. Anschließend nach dem
|
||
|
Benutzernamen.
|
||
|
\end{itemize}
|
||
|
|
||
|
% #>-----------------------------------------------------------------------------<#
|
||
|
\subsection{join}
|
||
|
\label{sec:text-processing.join}
|
||
|
|
||
|
Sicherlich kennen Sie das \textit{JOIN} Statement von SQL. Ähnlich zu dem
|
||
|
SQL-Statement verhält sich das Programm \textit{join}, jedoch mit einigen
|
||
|
Restriktionen.
|
||
|
|
||
|
\begin{itemize}[itemsep=0pt]
|
||
|
\item Die Spalten müssen nach dem Fremdschlüssel sortiert sein.
|
||
|
\item Beide Dateien müssen den gleichen Trenner verwenden.
|
||
|
\item Kann ein Fremdschlüssel nicht aufgelöst werden, wird eine Fehlermeldung
|
||
|
für die jeweilige Zeile ausgegeben.
|
||
|
\end{itemize}
|
||
|
|
||
|
Im folgenden Beispiel werden die Zeilen aus den Dateien \textit{/etc/passwd} und
|
||
|
\textit{/etc/group} über die Spalte \textit{GID} miteinander verbunden und
|
||
|
weitergeleitet. Dazu werden beide Dateien vorab sortiert. Können Fremdschlüssel
|
||
|
nicht aufgelöst werden wird die Fehlermeldung dazu nach \textit{/dev/null}
|
||
|
umgeleitet. Die Ausgabe wird anschließend noch einmal nach dem Benutzernamen
|
||
|
sortiert.
|
||
|
|
||
|
Informieren Sie sich in der Dokumentation über die Bedeutung der Optionen bzw.
|
||
|
Flags.
|
||
|
|
||
|
\begin{bashcode}
|
||
|
$ join -t ":" -1 3 -2 4 <(sort --field-separator=":" --key 3n /etc/group) <(sort --field-separator=":" --key 4n /etc/passwd) 2> /dev/null | sort --field-separator=":" --key 5
|
||
|
0:root:x::halt:x:7:halt:/sbin:/sbin/halt
|
||
|
0:root:x::operator:x:11:operator:/root:/sbin/nologin
|
||
|
0:root:x::root:x:0:root:/root:/bin/bash
|
||
|
...
|
||
|
\end{bashcode}
|