% <#>---------------------------------------------------------------------------<#> \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 <-----------------------------------------------------------------------------<# \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}