Viele Aenderungen

This commit is contained in:
rschaten 2004-12-02 13:54:06 +00:00
parent c0e556e25d
commit 15af99f29e
10 changed files with 873 additions and 57 deletions

0
TODO
View File

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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}

View File

@ -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.

View File

@ -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}

View File

@ -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\-\-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

View File

@ -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.