diff --git a/TODO b/TODO deleted file mode 100644 index e69de29..0000000 diff --git a/schmutzige_tricks.tex b/schmutzige_tricks.tex index a3760eb..92ba69b 100644 --- a/schmutzige_tricks.tex +++ b/schmutzige_tricks.tex @@ -27,6 +27,7 @@ haben. TODO!!! tar-Brücke %ssh 192.168.2.1 tar clf - / | (cd /mnt; tar xf - ) +%tar cf - $j | rsh $i "(mkdir -p $PWD ;cd $PWD; tar xf -)" \section{Binaries inside} @@ -49,7 +50,7 @@ TODO!!! Dateien, die es nicht gibt TODO!!! Speichern in nicht existente Dateien -\subsection{Subshell-Schleifen vermeiden} +\subsection{Subshell-Schleifen vermeiden}\label{subshellschleifen} Wir wollen ein Skript schreiben, das die \texttt{/etc/passwd} liest und dabei zählt, wie viele Benutzer eine UID kleiner als 100 haben. diff --git a/shell.tex b/shell.tex index 6d73131..7924c06 100644 --- a/shell.tex +++ b/shell.tex @@ -23,9 +23,15 @@ \usepackage{german} % deutsches Paket für Umlaute \usepackage[latin1]{inputenc} % Codepage latin1 -%\usepackage{graphicx} % Grafikpaket für Bilder laden +\usepackage{mathptmx} % Andere Schriften benutzen +\usepackage[scaled=.90]{helvet} +\usepackage{courier} + +%\usepackage[dvips]{graphicx} % Grafikpaket für Bilder laden +%\usepackage{epstopdf} % .eps bei Bedarf nach .pdf wandeln %\DeclareGraphicsRule{.tif}{bmp}{}{} % Grafikformate + \usepackage{tabularx} % für Tabellen über die Seitenbreite \usepackage{longtable} % für Tabellen über die Seitenbreite \usepackage{supertabular} % für Tabellen über die Seitenbreite @@ -93,8 +99,8 @@ TODO: Vern %\texttt{${}$Id${}$}\bigskip -Die aktuellste Version dieses Dokumentes befindet sich im World Wide Web auf -meiner Homepage (\texttt{http://www.schatenseite.de/}).\bigskip +Die aktuellste Version dieses Dokumentes befindet sich auf +\texttt{http://www.schatenseite.de/}.\bigskip Dieses Dokument ist entstanden, weil ich für mich selbst eine kompakte Übersicht zu diesem Thema haben wollte. Ich beabsichtige nicht, damit in diff --git a/tab_beisp_befehlsformen.tex b/tab_beisp_befehlsformen.tex index dc751dd..3859bef 100644 --- a/tab_beisp_befehlsformen.tex +++ b/tab_beisp_befehlsformen.tex @@ -12,6 +12,7 @@ \texttt{\$ cd; ls} & Sequentieller Ablauf \tabularnewline\STRUT \texttt{\$ (date; who; pwd) > logfile}\index{!>=\texttt{!>}} & Führt die Befehle in einer Subshell\index{Subshell} aus und lenkt alle Ausgaben um \tabularnewline\STRUT \texttt{\$ \{ date; who; pwd; \} > logfile}\index{!>=\texttt{!>}} & Lenkt alle Ausgaben um \tabularnewline\STRUT +\texttt{\$ time \{ date; who; pwd; \}}\index{time=\texttt{time}} & Summiert die Laufzeit der drei Kommandos\tabularnewline\STRUT \texttt{\$ sort }\textsl{Datei}\texttt{ | lp} & Sortiert die \textsl{Datei} und druckt sie\index{sort=\texttt{sort}} \tabularnewline\STRUT \texttt{\$ vi `grep -l ifdef *.c`}\index{*=\texttt{*}}\index{grep=\texttt{grep}} & Editiert die mittels grep gefundenen Dateien \tabularnewline\STRUT \texttt{\$ grep }\textsl{XX Datei}\texttt{ \&\& lp }\textsl{Datei}\index{grep=\texttt{grep}} & Druckt die \textsl{Datei}, wenn sie \textsl{XX} enthält\index{AND} \tabularnewline\STRUT diff --git a/tab_kommandos_uniq_parameter.tex b/tab_kommandos_uniq_parameter.tex new file mode 100644 index 0000000..38f73eb --- /dev/null +++ b/tab_kommandos_uniq_parameter.tex @@ -0,0 +1,19 @@ +% $Id$ +\begin{longtable}{|l|X|} +% KILLED & LINE!!!! \kill + \hline + \endfirsthead + \endhead + \endfoot + \hline + \endlastfoot + +\texttt{-c} & Anzahl der Vorkommnisse vor die Zeilen schreiben \tabularnewline\STRUT +\texttt{-d} & Nur doppelte Zeilen ausgeben, jede nur einmal \tabularnewline\STRUT +\texttt{-D} & Alle doppelten Zeilen ausgeben \tabularnewline\STRUT +\texttt{-f }\textsl{n} & Die ersten \textsl{n} Felder ignorieren \tabularnewline\STRUT +\texttt{-i} & Groß- / Kleinschreibung ignorieren \tabularnewline\STRUT +\texttt{-s }\textsl{n} & Die ersten \textsl{n} Zeichen ignorieren \tabularnewline\STRUT +\texttt{-u} & Nur einfach vorkommende Zeilen ausgeben \tabularnewline\STRUT +\texttt{-w }\textsl{n} & Nur die ersten \textsl{n} Zeichen betrachten +\end{longtable} diff --git a/tab_vordefinierte_variablen.tex b/tab_vordefinierte_variablen.tex index 3ee273c..fc17f13 100644 --- a/tab_vordefinierte_variablen.tex +++ b/tab_vordefinierte_variablen.tex @@ -17,7 +17,7 @@ \texttt{\$!} & Prozeßnummer des letzten Hintergrundprozesses \tabularnewline\STRUT \texttt{\$ERRNO} & Fehlernummer des letzten fehlgeschlagenen Systemaufrufs \tabularnewline\STRUT \texttt{\$IFS} & Feldseparator, wird beispielsweise beim Lesen mittels \texttt{read} benutzt \tabularnewline\STRUT -\texttt{\$PATH} & Pfad, in dem nach ausführbaren Kommandos gesucht wird. Mehrere Einträge werden durch Doppelpunkte getrennt angegeben \tabularnewline\STRUT +\texttt{\$PATH} & Pfad, in dem nach ausführbaren Kommandos gesucht wird\footnote{Mit dem Kommando \texttt{type}\index{type=\texttt{type}} findet man heraus, welches Executable tatsächlich verwendet wird.}. Mehrere Einträge werden durch Doppelpunkte getrennt angegeben \tabularnewline\STRUT \texttt{\$PWD} & Aktuelles Verzeichnis (wird durch \texttt{cd} gesetzt\footnote{Durch das Kommando \texttt{cd} wird das aktuelle Verzeichnis gewechselt, siehe Abschnitt \ref{cd}.}) \tabularnewline\STRUT \texttt{\$OLDPWD} & Vorheriges Verzeichnis (wird durch \texttt{cd} gesetzt) \end{longtable} diff --git a/was_ist_die_shell.tex b/was_ist_die_shell.tex index f3d4d29..b294641 100644 --- a/was_ist_die_shell.tex +++ b/was_ist_die_shell.tex @@ -69,7 +69,8 @@ Subshells zu vererben. Annehmlichkeiten wie die Korn-Shell, lehnt sich aber in der Syntax sehr stark an die Programmiersprache C an. Sie sollte nach Möglichkeit nicht zur Shell-Programmierung benutzt werden, da sie an vielen Stellen nicht so -reagiert, wie man es erwarten sollte. \end{itemize} +reagiert, wie man es erwarten sollte. +\end{itemize} \medskip\emph{Komfort-Shells:}\nopagebreak @@ -90,7 +91,7 @@ indizierte Arrays\index{Array}. TENEX-C-Shell\index{TENEX-C-Shell|textbf}\index{Shell>TENEX-C-|see{TENEX-C-Shell}} (\texttt{tcsh}\index{tcsh=\texttt{tcsh}|see{TENEX-C-Shell}}) verhält sich zur C-Shell wie die Bourne-Again-Shell zur Standard-Shell. Sie ist voll kompatibel, -bietet aber Kom\-fort-Funk\-tio\-nen wie Kommandozeilen-Editierung, +bietet aber Kom\-fort-Funk\-tio\-nen wie Kom\-man\-do\-zei\-len-Edi\-tie\-rung, programmierbare Auto-Completion\index{Auto-Completion}, Recht\-schreib\-hil\-fen und eine History. @@ -114,6 +115,6 @@ C- und schnell, bietet eine Lisp-ähnliche Sprache), \texttt{sash}\index{sash|textbf}\index{Shell>sash=\texttt{sash}|see{sash}} (System Administrator's Shell - eine statisch gelinkte Shell mit integrierten -Standard-Kommandos.). +Stan\-dard-Kom\-man\-dos.). Diese Liste ist bei weitem nicht vollständig. diff --git a/werkzeugkasten.tex b/werkzeugkasten.tex index f49d591..44c53dc 100644 --- a/werkzeugkasten.tex +++ b/werkzeugkasten.tex @@ -1,5 +1,5 @@ % $Id$ -\chapter{Nützliche Shell-Kommandos}\label{nuetzliche_shell-kommandos} +\chapter{Werkzeugkasten}\label{werkzeugkasten} Durch die gezeigten Steuerungsmöglichkeiten stehen dem Shell-Pro\-grammie\-rer Mög\-lich\-kei\-ten offen, fast alle gängigen Algorithmen zu implementieren. Es ist tatsächlich in der Shell möglich, Sortier- oder Suchfunktionen zu @@ -68,11 +68,30 @@ Dateien auf der Festplatte. \item \texttt{head} (\ref{head}): Dateianfang ausgeben \item \texttt{printf} (\ref{printf}): Formatierte Datenausgabe \item \texttt{read} (\ref{read}): Zeilen einlesen -\item \texttt{sort} (\ref{sort}): Zeilenweises Sortieren \item \texttt{tail} (\ref{tail}): Dateiende ausgeben \end{itemize} +\subsection{Dateiinhalte bearbeiten}\label{dateiinhalte} + +Natürlich bietet die Shell eine Reihe von Befehlen, um die Inhalte von Dateien +zu bearbeiten. Diese Auflistung ist in weiten Teilen deckungsgleich mit der +Liste der Tools zur Manipulation von Pipes, auch diese Kommandos kommen also +in mehreren Situationen zum Einsatz. + +\begin{itemize} +\item \texttt{awk} (\ref{awk}): In einer Pipe editieren +\item \texttt{cmp} (\ref{cmp}): Binäre Dateien vergleichen +\item \texttt{cut} (\ref{cut}): Teile einer Zeile ausschneiden +\item \texttt{diff} (\ref{diff}): Textdateien vergleichen +\item \texttt{paste} (\ref{paste}): Dateien zusammenführen +\item \texttt{sed} (\ref{sed}): In einer Pipe editieren +\item \texttt{sort} (\ref{sort}): Zeilenweises Sortieren +\item \texttt{tr} (\ref{tr}): Zeichen ersetzen +\item \texttt{uniq} (\ref{uniq}): Doppelte Zeilen suchen +\end{itemize} + + \subsection{Pfade und Dateien}\label{pfade_und_dateien} Eine der Hauptaufgaben von Shell-Skripten ist natürlich das Hantieren mit @@ -85,10 +104,12 @@ Datei kann viel mehr sein als nur ein paar Daten im Filesystem. \begin{itemize} \item \texttt{basename} (\ref{basename}): Den Namen einer Datei (ohne Pfad) ausgeben +\item \texttt{cd} (\ref{cd}): Verzeichnis wechseln \item \texttt{cp} (\ref{cp}): Dateien kopieren \item \texttt{chgrp} (\ref{chgrp}): Gruppen-ID einer Datei ändern \item \texttt{chmod} (\ref{chmod}): Zugriffsrechte einer Datei ändern \item \texttt{chown} (\ref{chown}): Eigentümer einer Datei ändern +\item \texttt{cmp} (\ref{cmp}): Binäre Dateien vergleichen \item \texttt{dirname} (\ref{dirname}): Den Pfad zu einer Datei (ohne den Namen) ausgeben \item \texttt{find} (\ref{find}): Dateien suchen \item \texttt{mkdir} (\ref{mkdir}): Verzeichnisse anlegen @@ -96,6 +117,7 @@ Datei kann viel mehr sein als nur ein paar Daten im Filesystem. \item \texttt{rm} (\ref{rm}): Dateien löschen \item \texttt{rmdir} (\ref{rmdir}): Verzeichnisse löschen \item \texttt{touch} (\ref{touch}): Eine leere Datei anlegen, bzw. das Zugriffsdatum einer Datei ändern +\item \texttt{which} (\ref{which}): Ausführbare Dateien suchen \item \texttt{xargs} (\ref{xargs}): Ausgaben eines Kommandos als Parameter eines anderen Kommandos benutzen \end{itemize} @@ -120,12 +142,14 @@ und ausgef kleinsten geeigneten Hammer nehmen. \begin{itemize} +\item \texttt{awk} (\ref{awk}): In einer Pipe editieren \item \texttt{cut} (\ref{cut}): Teile einer Zeile ausschneiden \item \texttt{grep} (\ref{grep}): In einer Pipe suchen \item \texttt{sed} (\ref{sed}): In einer Pipe editieren -\item \texttt{awk} (\ref{awk}): In einer Pipe editieren \item \texttt{sort} (\ref{sort}): Zeilenweises Sortieren \item \texttt{tee} (\ref{tee}): Datenstrom in einer Datei protokollieren +\item \texttt{tr} (\ref{tr}): Zeichen ersetzen +\item \texttt{uniq} (\ref{uniq}): Doppelte Zeilen suchen \item \texttt{wc} (\ref{wc}): Zeilen, Wörter oder Zeichen zählen \end{itemize} @@ -149,6 +173,7 @@ Verf \item \texttt{ps} (\ref{ps}): Prozeßliste ausgeben \item \texttt{pgrep} (\ref{pgrep}): Bestimmte Prozesse suchen \item \texttt{pkill} (\ref{pkill}): Bestimmte Prozesse töten +\item \texttt{trap} (\ref{trap}): Auf Signale reagieren \end{itemize} @@ -164,7 +189,154 @@ ausgiebigere Informationen empfehle ich entsprechende B %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{awk}\label{awk}\index{awk=\texttt{awk}|(textbf} -TODO!!! awk +Über die Skriptsprache \texttt{awk} wurden schon ganze Bücher geschrieben, eine +vollständige Beschreibung würde den Rahmen dieses Dokumentes bei weitem +sprengen. Hier werden nur ein paar grundlegende Techniken beschrieben, die +häufig im Zusammenhang mit Shell-Skripten auftauchen. + +Oben wurde \texttt{awk} `Skriptsprache' genannt. Das ist insofern richtig, als +daß es eine mächtige und komplexe Syntax zur Verfügung stellt, um Texte +automatisiert zu bearbeiten. Es fällt somit in die gleiche Tool-Kategorie wie +\texttt{sed} (Abschnitt \ref{sed}). + +Es unterscheidet sich aber in seinen grundlegenden Prinzipien entscheidend von +den meisten anderen Programmiersprachen: \texttt{awk} arbeitet `Datenbasiert'. +Das bedeutet, daß zunächst die Daten spezifiziert werden mit denen gearbeitet +werden soll, dann folgen die auszuführenden Kommandos. Das Prinzip wird schnell +klar, wenn man sich einige der Beispiele weiter unten ansieht. + +\subsubsection{Aufruf} + +Auch der Aufruf erfolgt analog zu \texttt{sed}: Bei einfachen Aufgaben kann das +\texttt{awk}-Programm direkt an der Kommandozeile mitgegeben werden, komplexere +Programme werden in Dateien gespeichert und von dort gelesen. + +Eine weitere Gemeinsamkeit ist die Art der Ein- und Ausgabe. Wenn eine +Eingabedatei angegeben wird, wird diese verarbeitet. Ansonsten wird die +Standard-Eingabe gelesen. Ausgaben erfolgen immer auf der Standard-Ausgabe. + +\footnotesize +\begin{listing}[2]{1} +# Aufruf als Filter: +kommando1 | awk '{ print $1; print $2 }' | kommando2 + +# Aufruf mit einer zu bearbeitenden Datei: +awk '{ print $1; print $2 }' datei.txt + +# In einem Skript kann das Kommando auch über mehrere Zeilen gehen: +awk ' +{ + print $1; + print $2; +}' datei.txt + +# Alternativ können die Kommandos auch in eine eigene Datei gespeichert und +# über den Parameter -f eingebunden werden: +awk -f script.awk datei.txt +\end{listing} +\normalsize + +Neben dem Parameter \texttt{-f} zum Einlesen der Programmdatei gibt es noch den +Parameter \texttt{-F} mit dem der Feld-Trenner angegeben werden kann. Die +folgende Zeile gibt beispielsweise alle Benutzernamen und deren User-IDs aus +der Doppelpunktseparierten Datei \texttt{/etc/passwd} aus: + +\texttt{awk -F: '\{ print \$1\dq hat ID \dq\$3 \}' /etc/passwd} + +\subsubsection{Muster und Prozeduren} + +Die Skripte für \texttt{awk} bestehen aus Blöcken von Mustern und Prozeduren. +Ein Block hat den folgenden Aufbau: + +\textsl{muster}\texttt{ \{ }\textsl{prozedur}\texttt{ \}} + +Dabei sind beide Bestandteile des Blockes Optional: Wird das Muster +weggelassen, wird die Prozedur auf alle Textbestandteile angewandt. Und wird +keine Prozedur angegeben, wird der betroffene Text einfach ausgegeben. + +Das Muster kann dabei auf verschiedene Weise angegeben werden: +\begin{itemize} +\item Als regulärer Ausdruck (siehe Abschnitt \ref{mustererkennung}), +eingebettet in Slashes: \texttt{/}\textsl{muster}\texttt{/} +\item Als relationaler Ausdruck, bei dem bestimmte Kriterien auf die +Eingabedaten zutreffen müssen. Mit \texttt{\$2>\$1} werden beispielsweise +Zeilen angesprochen, deren zweites Feld einen größeren Wert hat als das erste. +\item Mit Operatoren für das Pattern-Matching, ähnlich wie in Perl (\texttt{\~} +oder \texttt{!\~}) +\item \texttt{BEGIN} kennzeichnet Prozeduren, die vor der Bearbeitung anderer +Blöcke zum Tragen kommen sollen. +\item Analog dazu gibt es ein \texttt{END}, mit dem abschließende Aktionen +gekennzeichnet werden. +\end{itemize} + +Abgesehen von \texttt{BEGIN} und \texttt{END} können die Muster auch durch +logische Operatoren (\texttt{\&\&}, \texttt{||} oder \texttt{!}) kombiniert +werden. Durch Komma getrennt besteht die Möglichkeit, Wirkungsbereiche zu +definieren. + +Die Prozeduren können Variablen- oder Array-Zuweisungen, Ausgabeanweisungen, +Funktionsaufrufe oder Kontrollstrukturen enthalten. + +\subsubsection{Variablen} + +Es gibt in \texttt{awk} eine Reihe eingebauter Variablen, die in Mustern oder +Prozeduren verwendet werden können: + +\LTXtable{\textwidth}{tab_kommandos_awk_variablen.tex} + +Eigene Variablen können nach Belieben verwendet werden, siehe dazu das Beispiel +mit den TeX-Dateien weiter unten. + +\subsubsection{Beispiele} + +Hier ein paar Einfache Beispiele für Blocks aus Mustern und Prozeduren: + +\footnotesize +\begin{listing}[2]{1} +# Das erste Feld jeder Zeile ausgeben: +{ print $1 } + +# Alle Zeilen ausgeben, die 'regexp' enthalten: +/regexp/ + +# Das erste Feld jeder Zeile ausgeben, die 'regexp' enthält: +/regexp/ { print $1 } + +# Datensätze mit mehr als zwei Feldern auswählen: +NF > 2 + +# Das dritte und das zweite Feld jeder Zeile ausgeben, deren erstes Feld den +# String 'WICHTIG' enthält: +$1 ~ /WICHTIG/ { print $3, $2 } + +# Die Vorkommen von 'muster' zählen, und deren Anzahl ausgeben: +/muster/ { ++x } +END { print x } + +# Alle Zeilen mit weniger als 23 Zeichen ausgeben: +length($0) < 23 + +# Alle Zeilen ausgeben, die mit 'Name:' anfangen und exakt sieben Felder +# enthalten: +NF == 7 && /^Name:/ + +# Alle Felder der Eingabedaten zeilenweise in umgekehrter Reihenfolge ausgeben: +{ + for (i = NF; i >= 1; i--) + print $i +} + +# Die Größe aller TeX-Dateien addieren, die Summe in kB umrechnen und ausgeben, +# verarbeitet die Ausgabe von 'ls -l': +/.*tex/ { summe += $5 } +END { summe /= 1024; print "Die Größe aller TeX-Files beträgt", summe, "kB" } + +# Pipe-Separierte Liste aller gemounteten Partitionen und derer Füllstände +# ausgeben, verarbeitet die Ausgabe von 'df': +BEGIN { OFS="|" } +/^\/dev\// { print $1,$5 } +\end{listing} +\normalsize \index{awk=\texttt{awk}|)} @@ -220,6 +392,14 @@ werden. \index{cat=\texttt{cat}|)} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{cd}\label{cd}\index{cd=\texttt{cd}|(textbf} + +Mit dem Kommando \texttt{cd} wird das aktuelle Verzeichnis gewechselt. + +\index{cd=\texttt{cd}|)} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{chgrp}\label{chgrp}\index{chgrp=\texttt{chgrp}|(textbf} @@ -328,6 +508,14 @@ diese Datei nicht allgemein lesbar ist. \index{chpasswd=\texttt{chpasswd}|)} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{cmp}\label{cmp}\index{cmp=\texttt{cmp}|(textbf} + +TODO!!! cmp GNU? + +\index{cmp=\texttt{cmp}|)} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{cp}\label{cp}\index{cp=\texttt{cp}|(textbf} @@ -355,9 +543,20 @@ der Formate N, N-, N-M oder -M benutzt werden. \LTXtable{\textwidth}{tab_kommandos_cut_beispiele.tex} +Praktisch das Gegenstück zu \texttt{cut} ist \texttt{paste}, damit werden +Dateien in Spalten zusammengeführt. Nährers dazu in Abschnitt \ref{paste}. + \index{cut=\texttt{cut}|)} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{diff}\label{diff}\index{diff=\texttt{diff}|(textbf} + +TODO!!! diff + +\index{diff=\texttt{diff}|)} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{dirname}\label{dirname}\index{dirname=\texttt{dirname}|(textbf} @@ -437,7 +636,72 @@ Arith\-me\-tik-Ex\-pan\-sion (Siehe \ref{arithmetikexpansion}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{find}\label{find}\index{find=\texttt{find}|(textbf} -TODO!!! find +Auf einem modernen System sind nicht selten mehrere zehn- oder hunderttausend +Dateien vorhanden. Um eine bestimmte Datei anhand komplexer Kriterien ausfindig +zu machen benutzt man \texttt{find}. + +Bei einem Aufruf wird zuerst das zu durchsuchende Verzeichnis, dann die +Suchkriterien und eventuell abschließend die durchzuführenden Aktionen +angegeben. + +Die Angabe der Suchkriterien ist sehr vielseitig, hier werden nur die +wichtigsten Optionen beschrieben. Wie immer empfehle ich das Studium der +Man-Page oder eines entsprechenden Buches. + +\LTXtable{\textwidth}{tab_kommandos_find_parameter.tex} + +Die verschiedenen Suchkriterien können kombiniert werden. Mit \texttt{-a} oder +\texttt{-o} erreicht man eine logische AND- bzw. OR-Verknüpfung, mit einem +vorangestellten \texttt{!} können Kriterien negiert werden. Die AND-Verknüpfung +muß nicht explizit angegeben werden, wenn mehrere Kriterien verwandt werden. +Komplexere Ausdrücke können durch runde Klammern gruppiert werden, dabei ist +jedoch deren Sonderbedeutung in der Shell entsprechend zu quoten (Siehe +Abschnitt \ref{quoting}). + +Bei der Angabe numerischer Parameter zu den Suchkriterien wird normalerweise +nach dem exakten Wert gesucht. Statt eines einfachen \textsl{n} kann jedoch +auch \textsl{+n} oder \textsl{-n} angegeben werden, damit wird dann nach +Vorkommen größer bzw. kleiner als \textsl{n} gesucht. + +Da die reine Beschreibung der Parameter manchmal etwas verwirrend ist, folgen +hier ein paar praktische Beispiele: + +\footnotesize +\begin{listing}[2]{1} +# Suche alle Einträge in bzw. unter dem aktuellen Verzeichnis: +find . +# Suche alle normalen Dateien mit der Endung txt unter /home: +find /home -type f -name \*.txt +# Suche alle Einträge außer symbolischen Links, in die jeder schreiben darf: +find / \! -type l -perm 777 +# Suche alle Dateien unter dem Homeverzeichnis, deren Größe 10000000 Bytes +# übersteigt und gib sie ausführlich aus: +find ~ -size +10000000c -exec ls -l {} \; +# Suche alle Einträge im Homeverzeichnis, die innerhalb der letzten zwei Tage +# geändert wurden: +find ~ -mtime -2 +\end{listing} +\normalsize + +Wenn mittels \texttt{-exec} weitere Kommandos gestartet werden, sollte beachtet +werden daß mindestens ein Prozeß pro Fundstelle gestartet wird. Das kostet sehr +viel, unter Umständen macht der Einsatz von \texttt{xargs} (Abschnitt +\ref{xargs}) Sinn. + +Die Ausführung von \texttt{find} erzeugt unter Umständen sehr viel Last auf der +Festplatte, bei Netzlaufwerken auch Netzwerkbandbreite. In einigen Fällen +bieten sich alternative Suchverfahren an: + +\textbf{Alternative 1:} Falls man den Namen der zu suchenden Datei kennt, und +das Locate-System installiert ist kann man die Datei auch mittels +\texttt{locate} suchen. Das ist ressourcenschonender, da nicht `live' das +Filesystem durchforstet wird, sondern nur die Locate-Datenbank. Diese wird +allerdings im Regelfall nur einmal täglich aktualisiert, die Suche taugt nicht +für schnell wechselnde Bestände. + +\textbf{Alternative 2:} Sucht man nach einer ausführbaren Datei, die im Pfad +vorhanden ist (`Wo liegt eigentlich Firefox?'), dann sucht man mittels +\texttt{which} (Abschnitt \ref{which}). Siehe auch: Abschnitt \ref{beispiele_suchen_dateien}. @@ -473,11 +737,44 @@ werden allerdings nicht die letzten Zeilen angezeigt, sondern die ersten. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{kill}\label{kill}\index{kill=\texttt{kill}|(textbf} -TODO!!! kill +Die landläufige Annahme ist, daß man mit dem \texttt{kill}-Kom\-man\-do +Prozesse `umbringt'. Das ist zwar wahr, aber nicht die ganze Wahrheit. -Übrigens: Die landläufige Annahme ist, daß man mit dem -\texttt{kill}-Kom\-man\-do Prozesse `umbringt'. Das ist zwar wahr, aber nicht -die ganze Wahrheit. +Im Prinzip sendet \texttt{kill} lediglich ein Signal an einen Prozeß. Ohne +weitere Parameter ist das tatsächlich ein SIGTERM, das den Prozeß im Regelfall +dazu bewegt sich zu beenden. Jeder Admin kennt das Verfahren, einem hängenden +Prozeß mittels \texttt{kill -9} den Gnadenschuß zu geben. Die 9 steht dabei für +das Signal mit der Nummer 9, SIGKILL. Noch ein gebräuchliches Signal ist SIGHUP +(1), der `Hangup'. Historisch wurde das gesendet wenn die Leitung zum Rechner +aufgelegt wurde, mittlerweile ist es gängige Praxis damit einen Daemon neu zu +initialisieren. + +Daneben stehen noch eine Reihe weiterer Signale zur Verfügung. Mit \texttt{kill +-l} kann man sich eine Liste ansehen. + +Es gibt verschiedene Wege, das Signal abzusetzen. Welchen man wählt ist +Geschmackssache. Hier ein paar Beispiele: + +\footnotesize +\begin{listing}[2]{1} +# Die folgenden Befehle sind gleichwertig. Alle senden ein HUP an Prozeß-ID 42: +kill -1 42 +kill -HUP 42 +kill -SIGHUP 42 +kill -s 1 42 +kill -s HUP 42 +kill -s SIGHUP 42 + +# virtueller Selbstmord: Alle Prozesse umbringen, die man umbringen kann: +kill -9 -1 + +# SIGTERM an mehrere Prozesse senden: +kill 123 456 789 +\end{listing} +\normalsize + +Siehe auch: Das Beispiel `Fallensteller' in Abschnitt \ref{traps} zeigt, wie +ein Skript auf Signale reagieren kann. \index{kill=\texttt{kill}|)} @@ -509,14 +806,45 @@ zur Verf %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{ls}\label{ls}\index{ls=\texttt{ls}|(textbf} +Den Inhalt von Verzeichnissen im Dateisystem bringt man mit \texttt{ls} in +Erfahrung. Ein einfacher Aufruf listet lediglich die Dateinamen im aktuellen +oder angegebenen Verzeichnis auf, das Kommando hat aber auch sehr viele +Parameter mit denen sich die Ausgabe anpassen läßt. Hier sind die wichtigsten, +eine vollständige Auflistung bietet wie immer die Man-Page: + +\LTXtable{\textwidth}{tab_kommandos_ls_parameter.tex} + +Besonders informativ gibt sich der Parameter \texttt{-l}, da damit auch die +Eigentümer und die Berechtigungen der Dateien angezeigt werden. Die Ausgabe hat +die folgende Form: + +\texttt{-rw-r--r-- 1 rschaten users 6252 Nov 19 14:14 shell.tex} + Die linke Spalte der Ausgabe zeigt die bestehenden Berechtigungen. Es ist ein Block in der Form `drwxrwxrwx'. An Stelle des d können auch andere Buchstaben stehen, hier wird der Dateityp angegeben, also ob es sich um eine einfache Datei (-), ein Verzeichnis (d), einen Link (l) oder ähnliches\footnote{Siehe -Man-Page} handelt. An Stelle der rwx-Blöcke können auch Striche stehen, die -stehen für nicht gesetzte Attribute. +Man-Page} handelt. Die rwx-Blöcke geben die Dateiberechtigungen jeweils für den +Besitzer, die Gruppe und andere User an. Dabei steht das r für read, w für +write und x für execute. An ihrer Stelle können auch Striche stehen, die +repräsentieren nicht gesetzte Attribute. Die Datei im Beispiel ist also für +ihren Besitzer les- und schreibbar, für alle anderen nur lesbar. Die +Berechtigungen werden mit dem Kommando \texttt{chmod} (Abschnitt \ref{chmod}) +gesetzt. -TODO!!! ls +Die nächste Spalte stellt die Anzahl der Links dar, die auf diese Datei +verweisen, im Beispiel existiert die Datei an nur einer Stelle im Filesystem. + +Dann folgen der Benutzer und die Gruppe, denen die Datei gehört. Diese +Parameter werden mit \texttt{chown} (Abschnitt \ref{chown}) bzw. \texttt{chgrp} +(Abschnitt \ref{chgrp}) gesetzt. + +Es folgt die Größe der Datei in Bytes, sowie das Datum der letzten Änderung. +Liegt dieses mehr als ein halbes Jahr zurück wird an Stelle der Uhrzeit die +Jahreszahl angegeben, es gilt also Vorsicht walten zu lassen, wenn dieser Wert +in Skripten benutzt werden soll. + +Abschließend wird der Name der jeweiligen Datei ausgegeben. \index{ls=\texttt{ls}|)} @@ -545,6 +873,25 @@ wird der Vorgang interaktiv, vor jeder Dateibewegung wird nachgefragt. \index{mv=\texttt{mv}|)} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{paste}\label{paste}\index{paste=\texttt{paste}|(textbf} + +Während mit \texttt{cut} (Abschnitt \ref{cut}) Dateien spaltenweise zerlegt +werden, werden sie mit \texttt{paste} zusammengeführt. Die Dateinamen werden +als Parameter übergeben, woraufhin Zeile für Zeile die Inhalte aller Dateien zu +einer Tabelle gemacht werden. + +Die Spalten werden standardmäßig durch Tabulatorzeichen getrennt, man kann mit +dem Parameter \texttt{-d} auch ein oder mehrere andere Trennzeichen definieren. +Werden mehrere Zeichen angegeben, werden sie der Reihe nach zum trennen der +Spalten benutzt. + +Mit \texttt{-s} wird die Tabelle transponiert, also praktisch um 90 Grad +gedreht. + +\index{paste=\texttt{paste}|)} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{pgrep}\label{pgrep}\index{pgrep=\texttt{pgrep}|(textbf} @@ -640,7 +987,65 @@ dazu steht im Abschnitt %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{ps}\label{ps}\index{ps=\texttt{ps}|(textbf} -TODO!!! ps +Mit \texttt{ps} gibt man einen Schnappschuß des Zustandes der aktuell laufenden +Prozesse aus\footnote{Wenn man interaktiv den Zustand der laufenden Prozesse +beobachten möchte, benutzt man \texttt{top}, das eignet sich jedoch nicht zur +Shell-Programmierung und wird deshalb nicht ausführlich beschrieben.}. + +Ohne weitere Parameter listet \texttt{ps} alle Prozesse auf, die dem +aufrufenden Benutzer gehören und die mit dem aktuellen Terminal assoziiert +sind. Angezeigt werden dann die Prozeß-ID, das Terminal, die verbrauchte +CPU-Zeit und der Name des laufenden Kommandos. + +In Skripten möchte man üblicherweise feststellen, ob ein bestimmtes Kommando +aktiv ist, ob also zum Beispiel ein bestimmter Serverdienst läuft. Dazu macht +man \texttt{ps} über Optionen gesprächiger. + +Das Kommando versteht in der GNU-Version zwei unterschiedliche Arten von +Optionen. Den sogenannten Unix- bzw. Posix-Stil und den BSD-Stil. Zusätzlich +gibt es noch ausführliche Parameter, aber die sollen hier nicht beschrieben +werden. Die jeweiligen Formen stehen nicht auf allen Systemen zur Verfügung, +wenn ein Skript beispielsweise auch unter Solaris benutzt werden soll ist man +gezwungen, die Unix-Parametrisierung zu benutzen. + +Unix-Parameter zeichnen sich durch die übliche Angabe mit Bindestrich aus. +BSD-Pa\-ra\-me\-ter werden ohne Bindestrich angegeben, was neben den meisten +anderen Kommandos etwas ungewohnt aussieht. + +Es gibt sehr viele verschiedene Parameter, die beste Informationsquelle ist wie +immer die Man-Page bzw. ein entsprechendes Buch. Hier werden nur ein paar +typische Aufrufbeispiele gezeigt, deren Ausgabe sich jeder selber ansehen kann: + +\footnotesize +\begin{listing}[2]{1} +# Alle Prozesse auflisten, Unix-Syntax: +ps -e +ps -ef +ps -eF +ps -ely + +# Alle Prozesse auflisten, BSD-Syntax: +ps ax +ps axu + +# Prozeßbaum ausgeben. Das ist in Skripten weniger Sinnvoll, wird hier aber +# angegeben weil es so eine praktische Funktion ist... :-) +ps -ejH +ps axjf + +# Alle Prozesse ausgeben, die nicht dem Benutzer `root' gehören: +ps -U root -u root -N + +# Nur die Prozeß-ID von Syslog ausgeben: +ps -C syslogd -o pid= + +# Nur den Namen des Prozesses mit der ID 42 ausgeben: +ps -p 42 -o comm= +\end{listing} +\normalsize + +Für die Suche nach Prozessen bestimmten Namens steht auf manchen Systemen auch +das Kommando \texttt{pgrep} (Abschnitt \ref{pgrep}) zur Verfügung. Siehe auch: Abschnitt \ref{beispiele_suchen_prozesse}. @@ -755,7 +1160,203 @@ debuggen, da sowohl Ein- als auch Ausgaben in dem Logfile sichtbar sind. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{sed}\label{sed}\index{sed=\texttt{sed}|(textbf} -TODO!!! sed +Der `Stream Editor' \texttt{sed} stellt, ähnlich wie \texttt{awk} (Abschnitt +\ref{awk}) eigentlich eine eigene Skriptsprache dar. Er wird auch +`nicht-interaktiver Editor' genannt. Die Kommandos sind minimalistisch, aber +exakt auf die Aufgabe zugeschnitten. + +\texttt{sed} liest Zeilenweise aus einer Datei, wenn keine Datei angegeben +wurde wird von der Standard-Eingabe gelesen. Auf die eingelesenen Zeilen wird +dann ein mehr oder weniger kompliziertes \texttt{sed}-Skript angewendet, bevor +auf der Standard-Ausgabe die Resultate ausgegeben werden. + +Eine vollständige Beschreibung von \texttt{sed} würde an dieser Stelle den +Rahmen sprengen, es gibt aber im Handel gute Bücher zu dem Thema. Hier sollen +nur die gängigsten Kommandos und einige Anwendungsbeispiele genannt werden. + +\subsubsection{Aufruf} + +\footnotesize +\begin{listing}[2]{1} +# Aufruf als Stream-Editor: +kommando1 | sed 's/alt/neu/' | kommando2 + +# Aufruf mit einer zu bearbeitenden Datei: +sed 's/alt/neu/' datei.txt + +# Wenn mehr als ein Kommando ausgeführt werden soll, muß der Parameter -e +# verwendet werden: +sed -e 's/alt/neu/' -e '/loeschen/d' datei.txt + +# Man kann auch mehrere Kommandos mit einem -e aufrufen, wenn sie durch ein +# Semikolon getrennt werden: +sed 's/alt/neu/; /loeschen/d' datei.txt + +# In einem Skript kann das Kommando auch über mehrere Zeilen gehen: +sed ' +s/alt/neu/ +/loeschen/d' datei.txt + +# Alternativ können die Kommandos auch in eine eigene Datei gespeichert und +# über den Parameter -f eingebunden werden: +sed -f script.sed datei.txt +\end{listing} +\normalsize + +Neben den oben erwähnten Parametern kann \texttt{sed} auch mit \texttt{-n} +ruhig gestellt werden. Damit werden die Zeilen nur dann ausgegeben, wenn das +mittels `p' explizit gefordert wird. Die GNU-Version stellt noch ein paar +Parameter zur Verfügung, die Man-Page verrät näheres. + +\subsubsection{Addressierung} + +Durch die Adressierung können Befehle gezielt auf bestimmte Zeilen angewandt +werden. Dabei können einem Befehl keine, eine oder zwei Adressen mitgegeben +werden. + +Wenn keine Zeilen adressiert werden, wirkt der Befehl auf alle Zeilen. + +Wenn eine Adresse mitgegeben wird, wirkt der Befehl auf alle Zeilen die durch +diese Adresse angesprochen werden. Das können, zum Beispiel bei einem regulären +Ausdruck, auch mehrere Zeilen sein. + +Werden zwei Adressen angegeben, wirkt der Befehl auf die erste betroffene +Zeile, sowie auf alle weiteren bis zur zweiten angegebenen Zeile. Die beiden +Adressen müssen durch ein Komma getrennt angegeben werden. + +Die Auswahl der Zeilen kann durch ein an die Adresse angehängtes Rufzeichen +negiert werden, der Befehl wirkt dann also auf alle Zeilen die \textbf{nicht} +adressiert wurden. + +Aber wie sehen solche Adreßangeben aus? Die folgende Tabelle zeigt einige +Beispiele anhand des Kommandos `d', mit dem Zeilen gelöscht werden: + +\LTXtable{\textwidth}{tab_kommandos_sed_adressen.tex} + +Adressen können auch vor geschweiften Klammern stehen, dann wirken sie auf die +komplette Befehlsfolge innerhalb der Klammern. + +\subsubsection{Kommandos} + +Es gibt eine ganze Reihe von Kommandos, diese Beschreibung konzentriert sich +aber auf die wichtigsten `Brot und Butter-Kommandos'. In den Beispielen weiter +unten kommen auch andere Kommandos vor, die können bei Bedarf anhand der +einschlägigen Quellen nachgeschlagen werden. + +\LTXtable{\textwidth}{tab_kommandos_sed_kommandos.tex} + +Mit \texttt{s} wird substituiert. Das heißt, in der Eingabezeile wird nach +einem Muster gesucht, und im Erfolgsfall wird es ersetzt. Wichtigster +Modifikator für dieses Kommando ist \texttt{g}, damit wird `global' ersetzt, +falls mehrere Fundstellen in einer Zeile vorkommen. Der Aufruf sieht wie folgt +aus: + +\texttt{s/Suchmuster/Ersatzmuster/g} + +Im Ersatzmuster können auch Teile der Fundstelle wieder vorkommen, wenn sie +durch Klammern in einen Puffer kopiert werden: + +\texttt{s/Seite ([0-9]*) von ([0-9]*)/\textbackslash{}1 aus \textbackslash{}2/} + +Mit \texttt{y} hingegen werden einzelne Buchstaben durch andere vertauscht. Das +folgende Kommando wandelt alle eingehenden Kleinbuchstaben in Großbuchstaben +um\footnote{Umlaute und Sonderzeichen ausgeschlossen}: + +\texttt{y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/} + +Normalerweise werden alle Eingabezeilen nach der Bearbeitung wieder ausgegeben, +unabhängig davon ob sie verändert wurden oder nicht. Das Verhalten kann über +den Kommandozeilenparameter \texttt{-n} abgeschaltet werden. Da dann allerdings +nichts mehr ausgegeben wird kann durch ein an ein Kommando angehängtes +\texttt{p} bestimmt werden, daß die Veränderten Zeilen~--~und nur +die~--~ausgegeben werden. + +\subsubsection{Beispiele} + +Da es in diesem Text nicht um \texttt{sed}-Skripte, sondern um Shell-Skripte +gehen soll werden hier keine komplexen Sachen vorgestellt, sondern nur ein paar +Einzeiler. Nichtsdestotrotz können es auch diese unscheinbaren Aufrufe in sich +haben. + +\footnotesize +\begin{listing}[2]{1} +### SUCHEN UND ERSETZEN + +# Im kompletten Text 'rot' durch 'blau' ersetzen: +sed 's/rot/blau/' # Ersetzt nur das erste Vorkommen in jeder Zeile +sed 's/rot/blau/4' # Ersetzt nur das vierte Vorkommen in jeder Zeile +sed 's/rot/blau/g' # Ersetzt nur jedes Vorkommen in jeder Zeile + +# 'rot' durch 'blau' ersetzen, aber NUR in Zeilen die auch 'gelb' enthalten: +sed '/gelb/s/rot/blau/g' + +# 'rot' durch 'blau' ersetzen, AUSSER in Zeilen die auch 'gelb' enthalten: +sed '/gelb/!s/rot/blau/g' + +# 'rosa', 'hellrot' und 'magenta' durch 'pink' ersetzen: +sed 's/rosa/pink/g;s/hellrot/pink/g;s/magenta/pink/g' +gsed 's/rosa\|hellrot\|magenta/pink/g' # nur in GNU sed + +# Jede Zeile um fünf Leerzeichen einrücken: +# lies: 'ersetze jeden Zeilenanfang durch fünf Leerzeichen' +sed 's/^/ /' + +# Führende Blanks (Leerzeichen, Tabulatoren) von den Zeilenanfängen löschen: +# ACHTUNG: An Stelle des \t muß der Tabulator gedrückt werden, die Darstellung +# als \t versteht nicht jedes sed! +sed 's/^[ \t]*//' + +# Schliessende Blanks vom Zeilenende löschen, siehe oben: +sed 's/[ \t]*$//' + +# Führende und schließende Blanks löschen: +sed 's/^[ \t]*//;s/[ \t]*$//' + +# Wenn eine Zeile mit Backslash aufhört den Zeilenumbruch entfernen: +sed -e :a -e '/\\$/N; s/\\\n//; ta' + +### BESTIMMTE ZEILEN AUSGEBEN + +# Nur Zeile 42 ausgeben: +sed -n '42p' # Methode 1 +sed '42!d' # Methode 2 + +# Nur die Zeilen 23-42 ausgeben (inklusive): +sed -n '23,42p' # Methode 1 +sed '23,42!d' # Methode 2 + +# Von einem regulären Ausdruck bis zum Dateiende ausgeben: +sed -n '/regexp/,$p' + +# Den Bereich zwischen zwei regulären Ausdrücken ausgeben (inklusive): +sed -n '/rot/,/blau/p' + +# Nur Zeilen mit mindestens 42 Zeichen ausgeben: +sed -n '/^.\{42\}/p' + +# Nur Zeilen mit höchstens 42 Zeichen ausgeben: +sed -n '/^.\{42\}/!p' # Methode 1, analog zu oben +sed '/^.\{42\}/d' # Methode 2, einfachere Syntax + +### BESTIMMTE ZEILEN LÖSCHEN + +# Die ersten zehn Zeilen löschen: +sed '1,10d' + +# Die letzte Zeile löschen: +sed '$d' + +# Alles außer dem Bereich zwischen zwei regulären Ausdrücken ausgeben: +sed '/rot/,/blau/d' + +# Alle Leerzeilen löschen: +sed '/^$/d' # Methode 1 +sed '/./!d' # Methode 2 + +# Alle Leerzeilen am Dateianfang löschen: +sed '/./,$!d' +\end{listing} +\normalsize \index{sed=\texttt{sed}|)} @@ -882,6 +1483,113 @@ Referenzdatei angepa \index{touch=\texttt{touch}|)} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{tr}\label{tr}\index{tr=\texttt{tr}|(textbf} + +Will man ganze Worte oder komplexe Muster in Dateien oder Pipes suchen und +ersetzen, greift man üblicherweise zu \texttt{sed} (Abschnitt \ref{sed}). Für +einzelne Buchstaben nimmt man hingegen \texttt{tr}. + +Normalerweise wird \texttt{tr} mit zwei Zeichenketten als Parametern +aufgerufen und übernimmt die zu konvertierenden Daten von der Standard-Eingabe. +Jedes Zeichen im eingehenden Datenstrom wird anhand der beiden Zeichenketten +ersetzt, dabei wird das erste Zeichen der ersten Kette durch das erste Zeichen +der zweiten Kette ersetzt, das zweite durch das zweite, und so weiter. + +Ist die zweite Zeichenkette länger als die erste, werden überschüssige Zeichen +ignoriert. Ist die zweite Zeichenkette kürzer als die erste, wird ihr letztes +Zeichen so lange wiederholt bis sie gleich sind. Durch den Parameter +\texttt{-t} kann dieses Verhalten abgeschaltet werden, so daß überschüssige +Zeichen abgeschnitten werden. + +Mit dem Parameter \texttt{-c} wird die erste Zeichenkette `invertiert', es +werden also alle Zeichen ersetzt die nicht darin vorkommen. + +\texttt{tr} kann aber auch mit nur einer Zeichenkette aufgerufen werden, wenn +die Parameter \texttt{-d} oder \texttt{-s} benutzt werden. Mit \texttt{-d} +werden alle Zeichen aus dem Eingabestrom gelöscht, die in der Zeichenkette +vorkommen. Mit \texttt{-s} werden doppelt vorkommende Zeichen durch ein +einzelnes ersetzt. + +Die Zeichenketten an sich können übrigens nicht nur Buchstaben oder Zahlen +enthalten, sondern auch Sonderzeichen oder Zeichenklassen. Näheres dazu steht +in der Man-Page. + +Die folgenden Beispiele verdeutlichen die Anwendung: + +\footnotesize +\begin{listing}[2]{1} +text="Dies ist ein Testtext" + +# kleine Umlaute durch grosse ersetzen: +echo "$text" | tr aeiou AEIOU +# -> DIEs Ist EIn TEsttExt + +# Kleinbuchstaben durch Großbuchstaben ersetzen: +echo "$text" | tr a-z A-Z +# -> DIES IST EIN TESTTEXT + +# alle Vokale durch Unterstriche ersetzen: +echo "$text" | tr aeiouAEIOU _ +# -> D__s _st __n T_stt_xt + +# Großbuchstaben löschen: +echo "$text" | tr -d A-Z +# -> ies ist ein esttext + +# doppelte Buchstaben löschen: +echo "$text" | tr -s "a-zA-Z" +# -> Dies ist ein Testext + +# doppelte Buchstaben löschen, mit Zeichenklasse: +echo "$text" | tr -s "[:alpha:]" +# -> Dies ist ein Testext +\end{listing} +\normalsize + +\index{tr=\texttt{tr}|)} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{trap}\label{trap}\index{trap=\texttt{trap}|(textbf} + +Wie alle anderen Prozesse in einem Unix-System auch, so können auch +Shell-Skripte Signale empfangen. Diese können durch Kommandos wie \texttt{kill} +(Abschnitt \ref{kill}) geschickt worden sein, oder zum Beispiel durch einen +Tastatur-Interrupt. + +Mit \texttt{trap} kann ein Skript darauf vorbereitet werden, ein oder mehrere +Signale zu empfangen. Beim Aufruf wird eine Aktion mitgegeben, und eine oder +mehrere Bedingungen die zum Ausführen der Aktion führen sollen. Das folgende +Kommando gibt zm Beispiel eine Fehlermeldung aus wenn sein Skript ein Signal 1 +(HUP), 2 (INT) oder 15 (TERM) erhält: + +\texttt{trap 'echo \dq`basename \$0`: Ooops...\dq 1>\&2' 1 2 15} + +Die Zeile ist dem Beispiel aus Abschnitt \ref{traps} entnommen, dort findet +sich auch nochmal eine ausführliche Erklärung. + +\index{trap=\texttt{trap}|)} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{uniq}\label{uniq}\index{uniq=\texttt{uniq}|(textbf} + +Mit dem Kommando \texttt{uniq} werden doppelt vorkommende Zeilen in einer +Eingabedatei oder der eingehenden Pipe (Standard-Eingabe) bearbeitet. Per +default steht `bearbeitet' an dieser Stelle für `gelöscht', aber durch +Parameter kann dieses Verhalten angepaßt werden: + +\LTXtable{\textwidth}{tab_kommandos_uniq_parameter.tex} + +Achtung: \texttt{uniq} betrachtet beim Vergleich nur direkt aufeinander +folgende Zeilen. Sollen alle Duplikate Dateiweit betrachtet werden, bietet sich +ein `vorsortieren' mit \texttt{sort} (Abschnitt \ref{sort}) an, vielleicht +sogar ausschließlich ein \texttt{sort -u}. + +\index{uniq=\texttt{uniq}|)} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{wc}\label{wc}\index{wc=\texttt{wc}|(textbf} @@ -898,6 +1606,19 @@ Der Parameter \texttt{-L} gibt die L \index{wc=\texttt{wc}|)} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{which}\label{which}\index{which=\texttt{which}|(textbf} + +Sucht im Pfad (vordefinierte Variable +\texttt{\$PATH}\index{\$PATH=\texttt{\$PATH}}, siehe Abschnitt +\ref{vordefinierte_variablen}) nach einer Ausführbaren Datei. Wenn mehrere +Dateien auf das Suchwort passen wird die erste Fundstelle ausgegeben, also die +Datei die tatsächlich ausgeführt würde. Mit \texttt{-a} werden alle Fundstellen +ausgegeben. + +\index{which=\texttt{which}|)} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{who}\label{who}\index{who=\texttt{who}|(textbf} diff --git a/wie_sieht_ein_shell_skript_aus.tex b/wie_sieht_ein_shell_skript_aus.tex index b5d3776..1f04fea 100644 --- a/wie_sieht_ein_shell_skript_aus.tex +++ b/wie_sieht_ein_shell_skript_aus.tex @@ -2,27 +2,31 @@ \chapter{Wie sieht ein Shell-Skript aus?} Wie schon erwähnt, kann ein Shell-Skript beinahe alles, was eine `richtige' Programmiersprache auch kann. Bei der Entwicklung sollte man nur bedenken, daß -gerade die Ausführung von externen Kommandos~--~und das ist eine der -Standard-Techniken bei der Shell-Programmierung~--~nur sehr langsam vonstatten -geht. Für Anwendungen bei denen z. B. viele Rechnungen oder Stringbearbeitungen -gemacht werden müssen, sollte man also ggf. die Benutzung einer anderen -Sprache, beispielsweise Perl\index{Perl}, in Erwägung ziehen. +gerade die Aus\-füh\-rung von externen Kommandos~--~und das ist eine der +Standard-Techniken bei der Shell-Pro\-gram\-mie\-rung~--~nur sehr langsam +vonstatten geht. Für Anwendungen bei denen z. B. viele Rechnungen oder +Stringbearbeitungen gemacht werden müssen, sollte man also ggf. die Benutzung +einer anderen Sprache, beispielsweise Perl\index{Perl}, in Erwägung ziehen. In der Shell stehen viele Mechanismen zur Verfügung, die auch aus anderen Sprachen bekannt sind. Um den Umfang dieses Dokuments nicht zu sprengen, werden an dieser Stelle nur die wichtigsten vorgestellt. -\section{Grundsätzliches} +\section{HowTo} -Zunächst soll die Frage geklärt werden, wie man überhaupt ein ausführbares Shell-Skript schreibt. Dabei wird vorausgesetzt, daß dem Benutzer der Umgang mit mindestens einem Texteditor\index{Texteditor} (\texttt{vi}\index{vi=\texttt{vi}}, \texttt{emacs}\index{emacs=\texttt{emacs}} etc.) bekannt ist. +Zunächst soll die Frage geklärt werden, wie man überhaupt ein ausführbares +Shell-Skript schreibt. Dabei wird vorausgesetzt, daß dem Benutzer der Umgang +mit mindestens einem Texteditor\index{Texteditor} +(\texttt{vi}\index{vi=\texttt{vi}}, \texttt{emacs}\index{emacs=\texttt{emacs}} +etc.) bekannt ist. -Bei der Erstellung oder Bearbeitung von Shell-Skripten muß darauf geachtet -werden, daß sich keine CR/LF-Zeilenumbrüche einschleichen, wie dies leicht bei -der Benutzung von MS-DOS bzw. Windows-Systemen zur Bearbeitung von Skripten -über das Netzwerk passieren kann. - -\subsection{HowTo} - -Zunächst muß mit Hilfe des Editors eine Textdatei angelegt werden, in die der `Quelltext' geschrieben wird. Wie der aussieht, sollte man anhand der folgenden Abschnitte und der Beispiele im Anhang erkennen können. Beim Schreiben sollte man nicht mit den Kommentaren\index{Kommentar} geizen, da ein Shell-Skript auch schon mal sehr unleserlich werden kann. +Zunächst muß mit Hilfe des Editors eine Textdatei angelegt werden, in die der +`Quelltext' geschrieben wird. Dabei muß darauf geachtet werden, daß sich keine +CR/LF-Zei\-len\-um\-brü\-che einschleichen, wie dies leicht bei der Benutzung +von MS-DOS bzw. Windows-Systemen zur Bearbeitung von Skripten über das Netzwerk +passieren kann. Wie der Quelltext aussieht, sollte man anhand der folgenden +Abschnitte und der Beispiele im Anhang erkennen können. Beim Schreiben sollte +man nicht mit den Kommentaren\index{Kommentar} geizen, da ein Shell-Skript auch +schon mal sehr unleserlich werden kann. Nach dem Abspeichern der Datei unter einem geeigneten Namen\footnote{Bitte \emph{nicht} den Namen \texttt{test}\index{test=\texttt{test}} verwenden. Es @@ -32,25 +36,16 @@ ausgef zugleich einer der verwirrendsten Anfängerfehler. Mehr zu dem \texttt{test}-Kommando unter \ref{bedingungen}.} muß die sie ausführbar gemacht werden. Das geht mit dem Unix-Kommando -\texttt{chmod}\index{chmod=\texttt{chmod}}. Rechte können unter Unix getrennt -für den Benutzer (user, \texttt{u}), die Gruppe (group, \texttt{g}) oder andere -(others, \texttt{o}) vergeben werden. Außerdem kann man die Rechte für alle -Gruppen zusammen (all, a) setzen. Man kann getrennt die Rechte für das lesen -(read, \texttt{r}), das schreiben (write, \texttt{w}) und die Ausführung -(execution, \texttt{x}) vergeben. Damit die Datei für einen Benutzer wirklich -ausführbar ist, muß er das Lese- und das -Ausführungsrecht\index{Ausührungsrecht} haben. Um die Rechte zu setzen, muß man -\texttt{chmod} in Parametern mitgeben, worauf sich das Kommando bezieht, ob das -Recht gesetzt (\texttt{+}) oder weggenommen (\texttt{-}) werden soll, und -welche Rechte gemeint sind. Damit alle Benutzer das Skript ausführen dürfen, -benutzt man das Kommando \texttt{chmod ugo+rx name} oder einfach \texttt{chmod -+rx name}. Mit \texttt{chmod u+x name} hat nur der Besitzer der Datei -Ausführungsrechte. +\texttt{chmod}\index{chmod=\texttt{chmod}} und wird in Abschnitt \ref{chmod} +ausführlich beschrieben. An dieser Stelle reicht uns ein Aufruf in der Form +\texttt{chmod 755 dateiname}, um das Skript für alle Benutzer ausführbar zu +machen. -Um ein Shell-Skript ausführen zu können braucht es aus der Sicht des -ausführenden Benutzers mindestens die Rechte zum Lesen (r) und Ausführen (x). - -Dann kann das Skript gestartet werden. Da sich aus Sicherheitsgründen auf den meisten Systemen das aktuelle Verzeichnis nicht im Pfad des Benutzers befindet, muß man der Shell noch mitteilen, wo sie zu suchen hat: Mit \texttt{./name} wird versucht, im aktuellen Verzeichnis (\texttt{./}) ein Programm namens \texttt{name} auszuführen. +Dann kann das Skript gestartet werden. Da sich aus Sicherheitsgründen auf den +meisten Systemen das aktuelle Verzeichnis nicht im Pfad des Benutzers befindet, +muß man der Shell noch mitteilen, wo sie zu suchen hat: Mit \texttt{./name} +wird versucht, im aktuellen Verzeichnis (\texttt{./}) ein Programm namens +\texttt{name} auszuführen. Auf den meisten Systemen befindet sich im Pfad ein Verweis auf das Verzeichnis \texttt{bin} unterhalb des Home-Verzeichnisses eines Benutzers. Das bedeutet @@ -60,7 +55,7 @@ kann man an der Shell durch Eingabe von \texttt{echo \$PATH}\index{\$PATH=\texttt{\$PATH}} herausfinden. -\subsection{Rückgabewerte}\label{exitcode}\index{Rückgabewert|(textbf}\index{Exit-Code|see{Rückgabewert}}\index{Exit-Status|see{Rückgabewert}} +\section{Rückgabewerte}\label{exitcode}\index{Rückgabewert|(textbf}\index{Exit-Code|see{Rückgabewert}}\index{Exit-Status|see{Rückgabewert}} Wenn unter Unix ein Prozeß beendet wird, gibt er einen Rückgabewert (auch Exit-Code oder Exit-Status genannt) an seinen aufrufenden Prozeß zurück. So @@ -281,7 +276,9 @@ Ausdruck}}\index{Ausdruck|see{Regul wesentlich mehr Möglichkeiten als die relativ einfachen Wildcards für Dateinamen. -In der folgenden Tabelle wird gezeigt, in welchen Unix-Tools welche Zeichen zur Verfügung stehen. Eine ausführlichere Beschreibung der Einträge findet sich auf Seite \pageref{beschreibung_der_muster}. \nopagebreak +In der folgenden Tabelle wird gezeigt, in welchen Unix-Tools welche Zeichen zur +Ver\-fü\-gung stehen. Eine ausführlichere Beschreibung der Einträge findet sich +auf Seite \pageref{beschreibung_der_muster}. \nopagebreak \LTXtable{\textwidth}{tab_mustererkennung_muster.tex} Bei einigen Tools (\texttt{ex}, \texttt{sed} und \texttt{ed}) werden zwei Muster angegeben: Ein Suchmuster (links) und ein Ersatzmuster (rechts). Nur die folgenden Zeichen sind in einem Ersatzmuster gültig:\nopagebreak @@ -358,6 +355,54 @@ F \index{Arithmetik-Expansion|)} +\section{Eltern und Kinder: Prozeßordnung\label{prozessordnung}\index{Prozess|(textbf}\index{PID|see{Prozess}}\index{Parent-Prozess|see{Prozess}}\index{PPID|see{Prozess}}\index{Subshell|(textbf}} + +Jedes laufende Programm auf einem Unixoiden System besteht aus einem oder +mehreren Prozessen, die jeweils eine eigene Prozeß-ID (PID) haben. Erzeugt ein +Programm mehrere Prozesse, sind die zu einer Prozeßgruppe zusammengefaßt. Jeder +laufende Prozeß\footnote{Es gibt eine Ausnahme: der Init-Prozeß, der immer die +PID 1 hat, hat keine Eltern. Er stammt direkt vom Kernel ab.} verfügt über +genau eine Parent-Prozeß-ID (PPID). Das ist die ID des Prozesses, der den +jeweiligen Prozeß erzeugt hat. Man spricht in diesem Zusammenhang tatsächlich +von Eltern- bzw. Kind-Prozessen. + +Diese Zusammenhänge lassen sich sehr schön durch die Ausgabe des Kommandos +\texttt{pstree} oder \texttt{ps -efH} darstellen, letzteres zeigt auch gleich +die PIDs und die PPIDs an. + +Wenn in einer Shell ein Kommando gestartet wird, ist es ein Kind dieser Shell. +Wird ein Skript gestartet, öffnet es sich seine eigene Shell (siehe +\ref{auswahl_der_shell}) und führt sich innerhalb dieser aus. Die Shell des +Skriptes ist dabei ein Kind der interaktiven Shell, die einzelnen Kommandos des +Skriptes sind Kinder der Skript-Shell. + +Eine solche Shell-in-Shell-Umgebung wird `Subshell' genannt, dieser +Mechanismus~--~und somit auch der Begriff~--~tauchen immer wieder auf. + +Wichtig in diesem Zusammenhang ist das Verständnis für die Vererbung zwischen +den beteiligten Prozessen. Wenn in einer Shell eine Variable definiert und +exportiert wird, existiert diese auch für die Kind-Prozesse. Gleiches gilt +beispielsweise für einen Verzeichnis-Wechsel. Umgekehrt gilt dies nicht: ein +Prozeß kann die Umgebung des Parent-Prozesses nicht verändern. Das geschieht +nicht zuletzt aus Sicherheitsgründen so. + +Will man die Änderungen eines Skriptes übernehmen~--~beispielsweise wenn ein +Skript die Benutzerumgebung konfigurieren soll (.bashrc, .profile und +Konsorten)~--~muß das explizit angefordert werden. Dazu ruft man es mit einem +vorangestellten \texttt{source}\index{source=\texttt{source}} bzw. in der +Kurzform mit einem vorangestellten Punkt auf. Weiteres zu dem Thema findet sich +im Abschnitt \ref{source}. + +Besonders muß man diesen Umstand im Hinterkopf behalten, wenn mit +Pipelines\index{Pipe} (siehe Abschnitt \ref{befehlsformen}) gearbeitet wird. +Dabei werden auch Kommandos in Subshells ausgeführt, was dann dazu führt daß +Variablen belegt werden die dann nach Ausführung der Pipeline plötzlich wieder +leer sind. Die Abschnitte \ref{subshellschleifen} und \ref{daten_hochreichen} +widmen sich diesem mitunter recht ärgerlichen Thema. + +\index{Prozess|)}\index{Subshell|)} + + \section{Programmablaufkontrolle} Bei der Shell-Programmierung verfügt man über ähnliche Konstrukte wie bei anderen Programmiersprachen, um den Ablauf des Programms zu steuern. Dazu gehören Funktionsaufrufe, Schleifen, Fallunterscheidungen und dergleichen.\nopagebreak diff --git a/wofuer_shell_programmierung.tex b/wofuer_shell_programmierung.tex index 87dd2a5..e153ed7 100644 --- a/wofuer_shell_programmierung.tex +++ b/wofuer_shell_programmierung.tex @@ -1,6 +1,12 @@ % $Id$ \chapter{Wofür Shell-Programmierung?} +Natürlich stellt sich die Frage, in welchen Situationen ein Shell-Skript der +richtige Weg ist, und wann man vielleicht doch besser zu einer interpretierten +oder compilierten Sprache greift. + +\section{Wofür?} + Die Shell ist der perfekte Baukasten für das Unix-Paradigma `small is beautiful'. Die mitgelieferten Unix-Standardkommandos sind einfach gehalten, erledigen aber auf effiziente Weise die Arbeit für die sie programmiert wurden. @@ -23,3 +29,19 @@ Befehlen ausf Beispiel eine Audio-CD kopieren soll, sollte das Brennprogramm nur dann aufrufen, wenn der Einlesevorgang erfolgreich abgeschlossen wurde. +\section{Wofür nicht?} + +Ein Shell-Skript besteht aus einer Abfolge von System-Tool-Aufrufen. Das heißt, +für jeden Schritt in einem Skript wird ein neuer Prozeß gestartet. Das kostet +eine Menge Systemzeit, die Skripte laufen also vergleichsweise langsam. Für +komplexe, zeitkritische oder langwierige Aufgaben sollte man also besser zu +Perl, Python oder in Extremfällen zu C / C++ greifen. + +Shell-Skripte können als imperativ angesehen werden, für viele Aufgaben ist +aber ein objektorientierter Ansatz wesentlich geeigneter. Auch hier ist also +der Griff zu einer anderen Sprache angeraten. + +Es gibt zwar ein paar Tools\footnote{Zum Beispiel dialog im Textmodus, oder +xmessage unter X.}, mit denen auch Shell-Skripte eine grafische oder +textorientierte Benutzeroberfläche (GUI) bekommen können, aber das ist trotzdem +nicht das natürliche Terrain der Shell-Programmierung.