Shell-Programmierung/werkzeugkasten.tex

1918 lines
77 KiB
TeX

% $Id$
\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
schreiben. Leider kommt aber an dieser Stelle einer der bedeutendsten
Nachteile der Shell zum tragen: Die Geschwindigkeit.
In einem Shell-Skript wird für jedes externe Kommando\footnote{Externe
Kommandos sind solche, die nicht direkt in der Shell enthalten sind, für die
also ein eigenes Binary aufgerufen wird.} ein eigener Prozeß gestartet. Das
kostet natürlich Zeit und Speicher.
Zeitkritische Anwendungen sind also kein Einsatzgebiet für Shell-Skripte. Die
schreibt man besser in Perl, Python, oder noch besser in einer `compilierten'
Sprache wie C oder C++.
Es stehen jedoch an der Shell viele sehr nützliche externe Kommandos zur
Ver\-\-gung, die einem die Entwicklung entsprechender eigener Routinen
ersparen. Diese externen Kommandos sind zudem in anderen Sprachen geschrieben
worden, so daß sie schneller ablaufen als jedes Shell-Skript. Man kommt als
Shell-Programmierer nicht sinnvoll um den Einsatz dieser Programme herum.
In diesem Abschnitt sollen einige dieser Programme mit typischen
Einsatzmöglichkeiten vorgestellt werden. Eine vollständige Beschreibung wäre
(wenn überhaupt möglich) viel zu lang, um an dieser Stelle untergebracht zu
werden. \textbf{Dies ist also nur ein grober Überblick, nicht mal annähernd
eine vollständige Referenz!} Für ausführlichere Beschreibungen empfiehlt sich
das Studium der Man-Pages oder der Kauf eines entsprechenden Buches (Siehe
Anhang \ref{quellen}, `Quellen'). Am besten macht man natürlich beides. ;-)
Eine globale Beschreibung aller gängigen Kommandos würde den Rahmen dieses
Textes sprengen. Außerdem wäre es nicht leicht, das zu einer Aufgabe passende
Werkzeug zu finden. Die Werkzeuge nach Aufgaben zu sortieren fällt allerdings
auch nicht leicht. Die Entwickler der Kommandos versuchen, ihre Tools möglichst
universell einsetzbar zu halten, also gibt es keine 1:1-Beziehung zwischen
Problem und Lösung.
Um sowohl das Finden eines Werkzeugs zu einem gegebenen Problem als auch das
Finden einer Beschreibung zu einem gegebenen Werkzeug zu vereinfachen, und um
die oben beschriebene n:m-Beziehung abzubilden, werden hier also zunächst
typische Aufgaben beschrieben. Diese enthalten `Links' zu den in Frage
kommenden Werkzeugen. Danach gibt es eine alphabetische Aufzählung der
wichtigsten Kommandos.
\begin{dinglist}{43}
\item Viele der hier vorgestellten Kommandos stehen in erweiterten Versionen
zur Verfügung. Besonders auf GNU-Systemen~--~und somit auch auf Linux~--~gibt
es viele sehr nützliche Parameter, die man sich auf `standardkonformeren'
Systemen nur wünschen kann. Diese Vorteile sind allerdings mit Vorsicht zu
genießen: Wenn sie zum Einsatz kommen sind die entstehenden Skripte nicht mehr
plattformunabhängig.
Um Überraschungen zu vermeiden wurde versucht, diese Besonderheiten kenntlich
zu machen. Stellen mit einer Markierung wie in diesem Absatz sind also
besonders zu betrachten.
\end{dinglist}
\section{Nägel...}\label{naegel}
\subsection{Ein- und Ausgabe}\label{ein_und_ausgabe}
Praktisch jedes Skript verwendet in irgendeiner Form die Ein- oder Ausgabe. Sei
es in interaktiver Art auf dem Terminal, oder im Hintergrund auf Dateien.
An dieser Stelle sei darauf hingewiesen, daß es auf unixoiden Systemen nicht
nur Dateien im Sinne von `ein paar Kilobytes Daten, die irgendwo auf der
Festplatte rumliegen' gibt. Vielmehr findet man hier die Geräte des Rechners
als Dateien unter /dev. Der Kernel selbst stellt Schnittstellen in Form von
virtuellen Dateien unter /proc (ab Kernel 2.6 auch unter /sys) zur Verfügung.
Und schlußendlich können Prozesse sich sogenannte Named Pipes\index{Named Pipe}
anlegen, in die sie schreiben oder aus denen sie lesen.
Diese Kommandos sind also universell nützlich, nicht nur im Zusammenhang mit
Dateien auf der Festplatte.
\begin{itemize}
\item \texttt{cat} (\ref{cat}): Dateien einlesen und ausgeben
\item \texttt{date} (\ref{date}): Datum oder Zeit ausgeben
\item \texttt{echo} (\ref{echo}): Daten ausgeben
\item \texttt{grep} (\ref{grep}): In Dateien suchen
\item \texttt{head} (\ref{head}): Dateianfang ausgeben
\item \texttt{logger} (\ref{logger}): Text ins System-Log schreiben
\item \texttt{printf} (\ref{printf}): Formatierte Datenausgabe
\item \texttt{read} (\ref{read}): Zeilen einlesen
\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
Dateien. In diesem Abschnitt geht es allerdings nicht um den Umgang mit
Dateiinhalten, sondern vielmehr werden einige nützliche Tools im Umgang mit
Dateien an sich vorgestellt.
Auch hier gilt natürlich der Hinweis aus Abschnitt \ref{ein_und_ausgabe}: Eine
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
\item \texttt{mv} (\ref{mv}): Dateien verschieben
\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{type} (\ref{type}): Art eines Kommandos feststellen
\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}
\subsection{Pipes manipulieren}\label{pipes_manipulieren}\index{Pipe|(textbf}
Das Konzept der Pipes (Röhren) wird bereits in dem Kapitel über Befehlsformen
(\ref{befehlsformen}) vorgestellt. Im wesentlichen besteht es darin, daß Daten
von einem Programm an ein anderes weitergeleitet werden. Auf diese Weise
entsteht eine sogenannte \textit{Pipeline}\index{Pipeline} aus mehreren
Kommandos. Einige Kommandos sind für den Einsatz in einem solchen Konstrukt
prädestiniert, obwohl die meisten auch alleine eingesetzt werden können.
Übrigens gibt es einen goldenen Merksatz für die Auswahl einiger dieser Tools:
Benutze nicht \texttt{awk}, wenn Du \texttt{sed} benutzen kannst. Benutze
nicht \texttt{sed}, wenn Du \texttt{grep} benutzen kannst. Benutze nicht
\texttt{grep}, wenn Du \texttt{cut} benutzen kannst.
Der Grund dafür liegt darin, daß diese Programme bei jedem Einsatz gestartet
und ausgeführt werden müssen. Und man sollte sich um der Performance willen den
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{exec} (\ref{exec}): Dateideskriptoren umhängen
\item \texttt{grep} (\ref{grep}): In einer Pipe suchen
\item \texttt{sed} (\ref{sed}): 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}
\index{Pipe|)}
\subsection{Prozeßmanagement}\label{prozessmanagement}
Oft werden Shell-Skripte benutzt um Prozesse zu steuern oder zu überwachen. So
werden Systemdienste üblicherweise über die Init-Skripte hoch- oder
heruntergefahren. Es ist auch nicht sonderlich schwer, mit einem Skript einen
`Wachhund' zu implementieren, der regelmäßig kontrolliert ob ein Prozeß noch
läuft und ihn bei bedarf nachstartet.
Für Aufgaben in diesem Bereich stehen unter anderem die folgenden Kommandos zur
Verfügung.
\begin{itemize}
\item \texttt{exec} (\ref{exec}): Kommandos ausführen
\item \texttt{kill} (\ref{kill}): Signal an einen Prozeß schicken
\item \texttt{killall} (\ref{killall}): Signal an mehrere Prozesse schicken
\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
\item \texttt{wait} (\ref{wait}): Auf einen Prozeß warten
\end{itemize}
\section{... und Hämmer}\label{haemmer}
Um es noch einmal zu betonen: \textbf{Dies ist keine vollständige
Kommandoreferenz!} Es werden nur die wichtigsten Kommandos vorgestellt, und
deren Funktion wird in den meisten Fällen auch nur kurz angerissen. Für
ausgiebigere Informationen empfehle ich entsprechende Bücher (siehe Anhang
\ref{quellen}, `Quellen') und vor allem die Man-Pages.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{awk}\label{awk}\index{awk=\texttt{awk}|(textbf}
Ü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.
\begin{lstlisting}
# 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{lstlisting}
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:
\lstinline|awk -F: '{ print $1" hat ID "$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:
\begin{lstlisting}
# 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:", 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 }
# Alle Hosts aus der /etc/hosts anpingen, muß mit der /etc/hosts als
# Eingabedatei aufgerufen werden:
/^[^#]/ { system("ping -c 1 "$1) }
\end{lstlisting}
\index{awk=\texttt{awk}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{basename}\label{basename}\index{basename=\texttt{basename}|(textbf}
Dem Tool \texttt{basename} wird als Parameter ein Pfad zu einer Datei
übergeben. Der in der Angabe enthaltene Pfad wird abgeschnitten, nur der Name
der eigentlichen Datei wird zurückgegeben. Siehe auch \texttt{dirname}
(\ref{dirname}).
\index{basename=\texttt{basename}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{bc}\label{bc}\index{bc=\texttt{bc}|(textbf}
Bei \texttt{bc} handelt es sich, ähnlich wie bei \texttt{expr} um einen
Taschenrechner. Allerdings verfügt dieses Kommando um eine vergleichsweise
komplexe Syntax, die auch Berechnungen mit hoher Genauigkeit zulassen.
Für einfache Grundrechenaufgaben wie das Inkrementieren von Variablen sollte
man entweder die eingebaute Arithmetik-Expansion der Shell (Siehe
\ref{arithmetikexpansion}) oder das wesentlich ressourcenfreundlichere
\texttt{expr} (Siehe \ref{expr}) benutzen.
\index{bc=\texttt{bc}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{cat}\label{cat}\index{cat=\texttt{cat}|(textbf}
Auch \texttt{cat} ist ein oft unterbewertetes Tool. Seine Aufgabe besteht zwar
lediglich darin, etwas von der Standardeingabe oder aus einer Datei zu lesen,
und das dann auf der Standardausgabe wieder auszugeben. Allerdings leistet es
an vielen, teilweise sehr unterschiedlich gelagerten Aufgaben wertvolle
Dienste.
Durch Umlenklung der Ausgabe können Dateien erzeugt und erweitert werden. So
können mehrere Dateien per \lstinline|cat datei1.txt datei2.txt > datei.txt|
verkettet werden.
Außerdem kann man mit einem Aufruf in der Art
\lstinline!cat datei.txt | kommando! Daten an ein Programm übergeben, das nur
von der Standardeingabe lesen kann (Filter). Das geht zwar auch durch eine
Umleitung (siehe Abschnitt \ref{datenstrom}), wird aber in dieser Form von
vielen als lesbarer angesehen. Vorteil der Umleitungs-Methode ist, daß nicht
erst ein externes Kommando ausgeführt werden muß.
\begin{dinglist}{43}
\item GNU-\texttt{cat} verfügt über eine Reihe von Parametern, um die Ausgabe
zu formatieren, so können mit \texttt{-n} bzw. \texttt{-b} die Zeilen numeriert
werden, oder mit \texttt{-s} mehrere Zeilen zu einer einzigen zusammengefaßt
werden.
\end{dinglist}
\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}
Jede Datei gehört einem Benutzer und einer Gruppe. Letzteres läßt sich mit
\texttt{chgrp} einstellen. Als Parameter wird der Name oder die ID der Gruppe,
sowie ein oder mehrere Dateinamen übergeben. Verzeichnisse können rekursiv mit
dem Parameter \texttt{-R} bearbeitet werden.
Der Eingentümer der Datei wird mit \texttt{chown} (Abschnitt \ref{chown})
festgelegt.
\index{chgrp=\texttt{chgrp}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{chmod}\label{chmod}\index{chmod=\texttt{chmod}|(textbf}
In unixoiden Systemen verfügt jede Datei über eine Reihe von Attributen. Damit
kann eine Menge gemacht werden, für den vollen Funktionsumfang empfiehlt sich
das Studium der Man-Page oder einer umfangreicheren Kommandoreferenz. Hier nur
das wichtigste in Kürze:
Die Syntax lautet \texttt{chmod [options] mode file...}.
Die einzig wichtige Option ist, analog zu \texttt{chgrp} und \texttt{chown} der
Parameter \texttt{-R} für die rekursive Bearbeitung von Verzeichnissen.
In der Syntax steht `file' für einen oder mehrere Dateinamen.
Den Modus einer Datei sieht man, indem man \texttt{ls -l} darauf ansetzt, die
Ausgabe wird im entsprechenden Abschnitt (\ref{ls}) beschrieben.
Dort ist von den drei `rwx-Blöcken' die Rede, die die Berechtigungen für User
(u), Group (g) und Other (o) angeben. Genau die können mittels \texttt{chmod}
gesteuert werden. Zusätzlich gibt es hier noch die Angabe All (a), mit denen
die Rechte für alle Benutzer verändert werden können.
Hier wird der Modus gesteuert, indem direkt angegeben wird für wen welche
Rechte gelten sollen. Mit `+' werden die Rechte erweitert, `-' nimmt Rechte
und mit `=' werden die Rechte hart gesetzt.
\texttt{chmod u+x datei} macht die Datei für den Besitzer ausführbar. Mit dem
Parameter \texttt{u=rw,go=r} werden die Rechte auf `rw-r--r--' gesetzt, der
Besitzer kann lesen und schreiben, alle anderen nur lesen.
Neben dieser Art der Notation gibt es noch eine~--~wesentlich
gängigere~--~numerische Schreibweise. Dabei werden die Berechtigungen in Form
von Zahlen angegeben. Dabei werden drei Zahlen von eins bis sieben benutzt.
Deren Bedeutung ergibt sich, wenn man sich die drei Stellen `rwx' als Binärzahl
vorstellt. Das x steht an der niederwertigsten Stelle, erhält also den Wert 1.
Das w steht für die 2 und r für 4. In Summe ergeben diese Zahlen die
Berechtigung. Also ist `rwx' gleichbedeutend mit 4+2+1=7. `rw' entspricht
4+2=6. Die reine Leseberechtigung `r' bleibt als 4 stehen.
Zur Verdeutlichung ein paar Beispiele, wo es möglich ist in beiden Notationen:
\LTXtable{\textwidth}{tab_kommandos_chmod_beispiele.tex}
Am wichtigsten sind also die Aufrufe \lstinline|chmod 644 datei| und
\lstinline|chmod 755 datei|, je nachdem ob die Datei ausführbar sein soll oder
nicht.
\index{chmod=\texttt{chmod}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{chown}\label{chown}\index{chown=\texttt{chown}|(textbf}
Mit \texttt{chown} lassen sich Benutzer- und Gruppen-ID von Dateien und
Verzeichnissen festlegen. Mit dem Parameter \texttt{-R} sogar rekursiv für
Verzeichnisse.
Ein einzelner Parameter gibt die User-ID oder den Namen des zukünfigen
Benutzers an, in der Form name:gruppe können sowohl User- als auch Gruppen-ID
gleichzeitig geändert werden.
Will man lediglich die Gruppen-ID ändern, benutzt man das Kommando
\texttt{chgrp} (Abschnitt \ref{chgrp}).
\index{chown=\texttt{chown}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{chpasswd}\index{chpasswd=\texttt{chpasswd}|(textbf}
\begin{dinglist}{43}
\item Dies ist kein Standard-Kommando, es steht nicht auf allen Systemen zur
Verfügung.
\end{dinglist}
Mit diesem Kommando bietet sich dem Administrator des Systems die
Mög\-lich\-keit, scriptgesteuert die Paßwörter für neue Benutzer zu vergeben.
Manuell ändert man ein Paßwort mit dem Kommando
\texttt{passwd}\index{passwd=\texttt{passwd}}, allerdings löscht (flusht)
dieses Programm die Standard-Eingabe, bevor es das neue Paßwort erwartet. Somit
lassen sich Paßwörter mit \texttt{passwd} nur interaktiv ändern\footnote{Es
gibt auch einen anderen Weg: Man kann \texttt{passwd} auch mittels
\texttt{expect} fernsteuern. Allerdings ist diese Methode weniger elegant.}.
Das Kommando wird in der Form
\texttt{echo }\textit{name}\texttt{:}\textit{pass}\texttt{ | chpasswd}
aufgerufen. Es ist auch möglich, dem Programm eine Datei mit vielen Name /
Paßwort-Kombinationen an die Standard-Eingabe zu übergeben:
\texttt{cat }\textit{passwoerter.txt}\texttt{ | chpasswd}
Allerdings sollte dabei aus Sicherheitsgründen darauf geachtet werden, daß
diese Datei nicht allgemein lesbar ist.
\index{chpasswd=\texttt{chpasswd}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{cmp}\label{cmp}\index{cmp=\texttt{cmp}|(textbf}
Mit \texttt{cmp} werden zwei Dateien verglichen. Wenn die beiden Dateien
identisch sind gibt es keine Ausgabe, ansonsten wird die Position des ersten
Unterschiedes ausgegeben.
Einer der beiden anzugebenden Dateinamen kann auch durch \texttt{-} ersetzt
werden, dann wird die Standard-Eingabe mit der anderen angegebenen Datei
verglichen.
Mit dem Parameter \texttt{-l} werden alle abweichenden Bytes aufgelistet,
jeweils mit der Position (dezimal) und den beiden Bytes (oktal).
Durch \texttt{-s} läßt sich die Ausgabe von Unterschieden unterdrücken, der
Exit-Status gibt weiterhin das Ergebnis an.
\begin{dinglist}{43}
\item In der GNU-Version gibt es auch Parameter, mit denen Bereiche der Datei
vom Vergleich ausgeschlossen werden können (\texttt{-i}), oder mit denen nur
die ersten n Bytes der Dateien verglichen werden (\texttt{-n}).
\end{dinglist}
\index{cmp=\texttt{cmp}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{cp}\label{cp}\index{cp=\texttt{cp}|(textbf}
Mit \texttt{cp} werden Dateien kopiert. Die wichtigsten Optionen im
Zusammenhang mit Skripten sind \texttt{-f} und \texttt{-R}. Ersteres erzwingt
(force) das Kopieren, falls an der Zielstelle schon Dateien existieren werden
sie überschrieben. Letzteres ermöglicht ein rekursives Kopieren.
Verzeichnisse~--~auch leere~--~können nur mit \texttt{-R} kopiert werden.
\index{cp=\texttt{cp}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{cut}\label{cut}\index{cut=\texttt{cut}|(textbf}
Wie der Name (fast) schon sagt, kann man mit diesem Kommando Zeilen
zerschneiden. Mit den Parametern \texttt{-c} (Character) oder \texttt{-f}
(Field) wird bestimmt, in welcher Einheit die Schnittstellen abgesteckt werden
sollen. Falls die Zeilen in Felder zerteilt werden sollen, kann zusätzlich
mittels \texttt{-d} der Delimiter, also das Trennzeichen bestimmt werden. Wird
das nicht explizit getan, wird der Tabulator benutzt.
Dieser Definition folgt die Angabe des zu behaltenden Bereichs. Dafür kann eins
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{date}\label{date}\index{date=\texttt{date}|(textbf}
Ein einfacher Aufruf von \texttt{date} führt zu einem Ergebnis in der Art:
\lstinline|Do Jan 27 00:17:51 CET 2005|
Wie man sieht, wird das Datum auf dem System entsprechend der lokalen
Einstellungen ausgegeben. Auf diesem Rechner ist das deutsch, auf den meisten
Rechnern wird Englisch voreingestellt sein.
Neben dieser umfassenden aber starren Ausgabe ist es möglich, das Datum den
eigenen Wünschen entsprechend zu formatieren. Ein paar Beispiele:
\begin{lstlisting}
$ date +"%d.%m.%y, %H:%M"
27.01.05, 00:17
$ date +"Heute ist %A."
Heute ist Donnerstag.
$ date +"%A ist der %u. Tag der Woche."
Donnerstag ist der 4. Tag der Woche.
\end{lstlisting}
Die zur Formatierung bereitstehenden Platzhalter stehen in der Man-Page.
Eigentlich sollte \texttt{\%s} zur Ausgabe der Unix-Systemzeit
\footnote{Sekunden seit dem 01.01.1970, 0:00 Uhr} ein naheliegender Parameter
sein, leider steht er nur in der GNU-Version von \texttt{date} zur Verfügung.
\index{date=\texttt{date}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{diff}\label{diff}\index{diff=\texttt{diff}|(textbf}
Mit \texttt{diff} werden zwei Dateien verglichen, und die Änderungen auf der
Standardausgabe aufgelistet, die nötig sind um die erste an die zweite Datei
anzupassen.
Mit dem Parameter \texttt{-r} können auch ganze Verzeichnisse rekursiv
verglichen werden.
Die ausgegebenen Listen können mit dem \texttt{patch}-Kommando auf Dateien
angewandt werden um sie auf den geänderten Stand zu bringen.
\index{diff=\texttt{diff}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{dirname}\label{dirname}\index{dirname=\texttt{dirname}|(textbf}
Analog zu \texttt{basename} (\ref{basename}) gibt \texttt{dirname} nur die
Pfad-Komponente einer angegebenen Datei zurück.
\index{dirname=\texttt{dirname}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{echo}\label{echo}\index{echo=\texttt{echo}|(textbf}
Dies ist wohl der grundlegendste Befehl, der in einem Skript verwendet werden
kann. Er ist die Voraussetzung, um eines der wichtigsten Werkzeuge der
Kybernetik auch mittels eines Shell-Skriptes effizient umzusetzen: Hello World.
:-)
Die eigentliche Aufgabe dieses Befehls dürfte jedem bekannt sein, der sich bis
zu dieser Stelle durchgearbeitet hat. Allerdings wissen viele nicht, daß auch
der echo-Befehl über Parameter verfügt. Zumindest zwei davon erweisen sich in
der Praxis oft als sehr hilfreich:
\LTXtable{\textwidth}{tab_kommandos_echo_parameter.tex}
\begin{dinglist}{43}
\item Die Steuerzeichen, die nach einem \texttt{-e} angegeben werden können
sind nicht in allen Shells gleich. Das sollte berücksichtigt werden, wenn das
Skript portabel bleiben soll.
\end{dinglist}
\index{echo=\texttt{echo}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{eval}\label{eval}\index{eval=\texttt{eval}|(textbf}
Die Wirkungsweise von \texttt{eval} läßt sich wohl am ehesten durch ein kleines
Beispiel erklären:
\LTXtable{\textwidth}{tab_kommandos_eval_beispiel.tex}
Bevor eine Zeile in der Shell tatsächlich ausgeführt wird, wird sie von der
Shell expandiert, bzw. evaluiert. Der letzte Begriff deutet schon an was damit
gemeint ist: Enthaltene Variablennamen werden durch ihre Werte ersetzt.
Das Kommando \texttt{eval} führt die Zeile die durch die Expansion entstanden
ist noch einmal aus. So ist es möglich, Variablennamen aus den Inhalten anderer
Variablen zu bilden.
Eine wichtige Anwendung für dieses Kommando ist der Fall, wenn eigentlich ein
Array\index{Array} gebraucht würde. Wenn \texttt{arr} der Name des Arrays und
\texttt{index} der Name der Variablen ist, die den Index des auszugebenden
Elementes enthält, dann kann durch die folgende Zeile der Inhalt eines
Array-Elementes ausgegeben werden:
\lstinline|eval echo \$arr$index|
\index{eval=\texttt{eval}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{exec}\label{exec}\index{exec=\texttt{exec}|(textbf}
Dieses Kommando hat zwei wesentliche Einsatzgebiete:
Wenn das laufende Skript nur benutzt wird um ein anderes Programm zu starten,
beispielsweise um die Umgebungsvariablen geeignet zu belegen, sollte das
Programm mit \texttt{exec} ausgeführt werden. Der Effekt ist, daß sich die
Shell in der das Skript ausgeführt wird beendet und die Kontrolle vollständig
an das neue Programm übergibt. So vermeidet man einen überflüssigen Prozeß, der
lediglich auf die Beendigung seiner Kinder wartet.
Der zweite Anwendungsfall ist etwas für den fortgeschrittenen Benutzer: Man
kann mit \texttt{exec} Dateideskriptoren umhängen. Damit ist gemeint, daß zum
Beispiel die Standardausgabe mittels \lstinline|exec >5| auf den
Dateideskriptor mit der Nummer 5 gelegt werden kann. Auf diesem Weg kann mit
mehreren Datenströmen jongliert werden. Die Beispiele in Anhang
\ref{dateien_die_es_nicht_gibt} verdeutlichen die Anwendung.
\index{exec=\texttt{exec}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{expr}\label{expr}\index{expr=\texttt{expr}|(textbf}
Mit dem Kommando \texttt{expr} verfügt die Shell praktisch über einen
Taschenrechner für einfache Berechnungen. Für komplexe Aufgaben bietet sich das
Tool \texttt{bc} an, näheres dazu steht in Abschnitt \ref{bc}.
Genau genommen kann man mit \texttt{expr} nicht nur Berechnungen
durch\-füh\-ren, sondern ganz allgemein `Ausdrücke evaluieren'. Damit ist
gemeint, daß es zum Beispiel auch Operatoren für Pattern-Matching gibt. Die
wichtigsten Operatoren lauten wie folgt:
\LTXtable{\textwidth}{tab_kommandos_expr_parameter.tex}
Bei einigen Sonderzeichen ist deren Bedeutung in der Shell zu berücksichtigen,
sie sind also durch Anführungszeichen oder Backslashes zu quoten:
\lstinline|i=`expr $i \* 3`|.
Eine andere Möglichkeit für einfache Rechnungen besteht in der sogenannten
Arith\-me\-tik-Ex\-pan\-sion (Siehe \ref{arithmetikexpansion}).
\index{expr=\texttt{expr}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{find}\label{find}\index{find=\texttt{find}|(textbf}
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:
\begin{lstlisting}
# 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{lstlisting}
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}) oder \texttt{type} (\ref{type}).
Siehe auch: Abschnitt \ref{beispiele_suchen_dateien}.
\index{find=\texttt{find}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{grep}\label{grep}\index{grep=\texttt{grep}|(textbf}
Das Tool \texttt{grep} stammt aus dem Standard-Repertoire eines jeden
Sys\-tem\-ad\-mi\-ni\-stra\-tors. Mit seiner Hilfe kann in einer oder mehreren
Dateien, oder eben auch in einem Datenstrom nach dem Auftreten bestimmter
regulärer Ausdrücke (siehe \ref{mustererkennung}) gesucht werden.
Die folgende Tabelle stellt einige der vielen Parameter vor:
\LTXtable{\textwidth}{tab_kommandos_grep_parameter.tex}
Siehe auch: Abschnitt \ref{beispiele_suchen}.
\index{grep=\texttt{grep}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{head}\label{head}\index{head=\texttt{head}|(textbf}
\texttt{head} ist das Gegenstück zu \texttt{tail} (Siehe \ref{tail}). Hier
werden allerdings nicht die letzten Zeilen angezeigt, sondern die ersten.
\index{head=\texttt{head}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{kill}\label{kill}\index{kill=\texttt{kill}|(textbf}
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:
\begin{lstlisting}
# 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{lstlisting}
Siehe auch: Das Beispiel `Fallensteller' in Abschnitt \ref{traps} zeigt, wie
ein Skript auf Signale reagieren kann.
\index{kill=\texttt{kill}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{killall}\label{killall}\index{killall=\texttt{killall}|(textbf}
Im Abschnitt über \texttt{kill} (\ref{kill}) wird beschrieben, wie man ein
Signal an einen Prozeß schickt, dessen ID bekannt ist. Kennt man die ID nicht,
oder will man das Signal an mehrere Prozesse schicken, kann dieses Kommando auf
vielen Systemen eine große Hilfe darstellen.
Mit dem Parameter \texttt{-i} wird vor jedem Signal interaktiv gefragt, ob es
geschickt werden soll. Mit \texttt{-v} wird angegeben, ob die Signale
erfolgreich versandt wurden, \texttt{-q} hingegen unterdrückt die Ausgaben.
Da ein Prozeß nach einem Signal nicht notwendigerweise sofort stirbt, gibt es
eine Option \texttt{-w}. Diese Veranlaßt \texttt{killall} zu warten, bis alle
Empfänger tot sind. Dieser Parameter ist allerdings mit Vorsicht zu genießen:
Wenn der Prozeß sich weigert zu sterben, wartet \texttt{killall} ewig.
Eine ähnliche Funktionalität bietet auch das Kommando \texttt{pkill} (Abschnitt
\ref{pkill}), allerdings hat \texttt{killall} den Vorteil daß es auf mehr
Systemen zur Verfügung steht.
\index{killall=\texttt{killall}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{logger}\label{logger}\index{logger=\texttt{logger}|(textbf}
Mit \texttt{logger} werden Nachrichten an die Log-Mechanismen des Systems
geschickt. So können auch unbeobachtet laufende Skripte über ihr tun
informieren.
Der zu loggende Text wird einfach als Parameter übergeben.
\begin{dinglist}{43}
\item Die GNU-Version verfügt über einige Parameter, unter anderem kann die
Nachricht mit \texttt{-s} parallel zum System-Log auch auf der
Standard-Fehlerausgabe ausgegeben werden.
\end{dinglist}
\index{logger=\texttt{logger}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\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.
\begin{dinglist}{43}
\item Einige der folgenden Parameter entsprechen nicht dem allgemeinen
Standard:
\end{dinglist}
\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:
\lstinline|-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. 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.
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}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{mkdir}\label{mkdir}\index{mkdir=\texttt{mkdir}|(textbf}
Mit diesem Kommando werden Verzeichnisse angelegt. Dabei kann mit \texttt{-m}
angegeben werden, welche Berechtigungen das Verzeichnis bekommen soll. Mit
\texttt{-p} werden bei Bedarf auch Parent-Verzeichnisse angelegt, es entsteht
also ein kompletter Pfad.
Entfernen lassen sich Verzeichnisse mit \texttt{rmdir} (Abschnitt \ref{rmdir}).
\index{mkdir=\texttt{mkdir}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{mv}\label{mv}\index{mv=\texttt{mv}|(textbf}
Dateien und Verzeichnisse können mit dem Kommando \texttt{mv} verschoben
werden. Falls am Ziel schon Dateien existieren erzwingt der Parameter
\texttt{-f} die Aktion, die alten Dateien werden überschrieben. Mit \texttt{-i}
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}
\begin{dinglist}{43}
\item Dies ist kein Standard-Kommando, es steht nicht auf allen Systemen zur
Verfügung.
\end{dinglist}
Eine häufig wiederkehrende Aufgabe ist es, zu sehen ob ein bestimmter
Prozeß existiert oder nicht. Falls das Kommando \texttt{pgrep} zur Verfügung
steht, kannn man auf das Konstrukt mit \texttt{ps} und \texttt{grep}
verzichten. Der folgende Aufruf liefert alle Prozeß-IDs, deren Name httpd
enthält, inclusive des vollen Kommandos:
\lstinline|pgrep -lf httpd|
Über weitere Parameter läßt sich genauer spezifizieren, wonach gesucht werden
soll, hier die wichtigsten:
\LTXtable{\textwidth}{tab_kommandos_pgrep_parameter.tex}
Die Ausgabe enthält per Default nur die Prozeß-IDs der Fundstellen. Diese läßt
sich als Parameter für andere Programme benutzen. Das folgende Beispiel liefert
detaillierte Informationen über alle xterm-Prozesse:
\lstinline|ps -fp $(pgrep -d, -x xterm)|
Siehe auch: Abschnitt \ref{beispiele_suchen_prozesse}.
\index{pgrep=\texttt{pgrep}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{pkill}\label{pkill}\index{pkill=\texttt{pkill}|(textbf}
\begin{dinglist}{43}
\item Dies ist kein Standard-Kommando, es steht nicht auf allen Systemen zur
Verfügung.
\end{dinglist}
Dieses Kommando ist eng verwandt mit \texttt{pgrep} (Siehe Abschnitt
\ref{pgrep}), es versteht im Wesentlichen die gleichen Parameter. Allerdings
werden die Fundstellen hier nicht ausgegeben. Wie der Name schon andeutet,
werden hiermit Prozesse umgebracht. Da man hier mit einem Kommando unter
Umständen viele Prozesse beendet, sollten \textbf{die verwendeten Parameter
genau unter die Lupe} genommen werden, um `Kollateralschäden' zu vermeiden. :-)
Es besteht auch die Möglichkeit, den Prozessen andere Signale zuzuschicken,
diese Funktion wird im Abschnitt zu \texttt{kill} (\ref{kill}) näher
beschrieben. Das folgende Kommando veranlaßt beispielsweise den Syslog-Daemon,
seine Konfiguration neu einzulesen:
\lstinline|pkill -HUP syslogd|
Das Kommando \texttt{killall} (Abschnitt \ref{killall}) bietet eine ähnliche
Funktionalität, allerdings fehlen ihm einige Parameter. Trotzdem sollte im
Zweifel \texttt{killall} benutzt werden, da es auf mehr Systemen zur Verfügung
steht.
\index{pkill=\texttt{pkill}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{printf}\label{printf}\index{printf=\texttt{printf}|(textbf}
Analog zum gleichnamigen Befehl in Programmiersprachen wie Perl oder C dient
\texttt{printf} der formatierten Ausgabe von Daten. Als Parameter wird ein
sogenannter Format-String und eine Liste von auszugebenden Daten mitgegeben.
Dabei enthält der Format-String eine Reihe von Platzhaltern, die nach
bestimmten Regeln durch die Daten ersetzt werden.
Der Format-String folgt im Wesentlichen den gleichen Regeln wie in der
C-Version. Näheres dazu erfährt man mit \lstinline|man 3 printf|.
Hier die wichtigsten Parameter für den Format-String:
\LTXtable{\textwidth}{tab_kommandos_printf_parameter.tex}
Besonders nützlich ist dieses Kommando bei der tabellarischen Ausgabe von
Daten. Im folgenden Beispiel werden alle Benutzernamen, deren
Home-Verzeichnisse und Default-Shells aus der Datei \texttt{/etc/passwd}
extrahiert und übersichtlich ausgegeben:
\begin{lstlisting}
#!/bin/sh
IFS=:
while read user pass uid gid name home shell; do
printf "%-15s %-25s %s\n" $user $home $shell
done < /etc/passwd
\end{lstlisting}
Zur Erinnerung: Die vordefinierte Variable
\texttt{\$IFS}\index{\$IFS=\texttt{\$IFS}} ist der Feld-Separator, die
Eingabezeilen werden also als Doppelpunkt-separierte Liste gesehen. Näheres
dazu steht im Abschnitt über vordefinierte Variablen
(\ref{vordefinierte_variablen}).
\index{printf=\texttt{printf}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{ps}\label{ps}\index{ps=\texttt{ps}|(textbf}
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.
\begin{dinglist}{43}
\item 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.
\end{dinglist}
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:
\begin{lstlisting}
# 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{lstlisting}
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}.
\index{ps=\texttt{ps}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{read}\label{read}\index{read=\texttt{read}|(textbf}
Mit dem Kommando \texttt{read} kann man Eingaben von der
Standard-Eingabe\index{Standard-Eingabe} lesen. Dabei wird üblicherweise einer
oder mehrere Variablennamen übergeben. Dem ersten Namen wird das erste
eingegebene Wort zugewiesen, dem zweiten das zweite Wort usw. Dem letzen
Variablennamen wird der verbleibende Rest der Eingabezeile zugewiesen. Wenn
also nur ein Variablenname angegeben wird, erhält dieser die komplette
Eingabezeile. Wenn weniger Worte gelesen werden als Variablen angegeben sind,
enthalten die verbleibenden Variablen leere Werte. Als Wort-Trennzeichen dienen
alle Zeichen, die in der vordefinierten Variable
\texttt{\$IFS}\index{\$IFS=\texttt{\$IFS}} enthalten sind (siehe Abschnitt
\ref{vordefinierte_variablen}).
Wenn keine Variablennamen angegeben werden, wird die Eingabe in der Variable
\texttt{REPLY} abgelegt.
Sonderzeichen können während der Eingabe normalerweise mittels eines Backslash
vor der Interpretation geschützt werden. Ein Backslash vor einem Newline
bewirkt also eine mehrzeilige Eingabe. Dieses Verhalten kann mit dem Parameter
\texttt{-r} abgeschaltet werden.
\begin{dinglist}{43}
\item Normalerweise wird eine Eingabezeile mit einem Newline abgeschlossen. Mit
dem Parameter \texttt{-d} ist es möglich, ein anderes Zeilenendezeichen
anzugeben. Beispielsweise liest \lstinline|read -d " " var| alle Zeichen bis
zum ersten Leerzeichen in die Variable \texttt{var} ein.
Wenn nur eine bestimmte Zahl von Zeichen gelesen werden soll, kann diese durch
den Parameter \texttt{-n} angegeben werden. Der Befehl
\lstinline|read -n 5 var| liest die ersten fünf Zeichen in die Variable
\texttt{var} ein. Demzufolge kann ein Skript durch ein \lstinline|read -n 1|
dazu gebracht werden, auf einen einzelnen Tastendruck~--~nicht zwingend ein
Return~--~zu warten.
Mit dem Parameter \texttt{-p} kann man einen Prompt, also eine
Eingabeaufforderung ausgeben lassen. \lstinline|read -p "Gib was ein:" var|
schreibt also erst den Text \textit{Gib was ein:} auf das Terminal, bevor die
Eingaben in die Variable \texttt{var} übernommen werden. Dieser Prompt wird nur
an einem interaktiven Terminal ausgegeben, also nicht in einem Skript das seine
Eingaben aus einer Datei oder aus einem Stream erhält.
Wenn die Eingabe von einem Terminal kommt und nicht auf dem Bildschirm
erscheinen soll, zum Beispiel bei Paßwortabfragen, kann die Ausgabe mit dem
Parameter \texttt{-s} (Silent) unterdrückt werden.
Mit \texttt{-t} kann ein Time-Out definiert werden, nach dessen Ablauf das
Kommando mit einem Fehler abbricht. Dieser Parameter ist nur bei interaktiver
Eingabe oder beim Lesen aus einer Pipe aktiv.
\end{dinglist}
Der Rückgabewert des \texttt{read}-Kommandos ist 0, es sei denn es trat ein
Timeout oder ein EOF auf.
\index{read=\texttt{read}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{rm}\label{rm}\index{rm=\texttt{rm}|(textbf}
Mit diesem Kommando können Dateien und Verzeichnisse gelöscht werden. Dabei
kann man vorsichtig vorgehen, indem man mit \texttt{-i} dafür sorgt, daß jeder
Löschvorgang be\-stä\-tigt werden muß. Oder rabiat, indem man mit \texttt{-f}
das Löschen erzwingt.
Verzeichnisse können mit dem Parameter \texttt{-R} entfernt werden, im
Gegensatz zu \texttt{rmdir} werden dann auch sämtliche enthaltenen Dateien und
Unterverzeichnisse gelöscht.
\begin{dinglist}{43}
\item Die GNU-Version von \texttt{rm} unterstützt zusätzlich den Parameter
\texttt{-v}, mit dem jeder Löschvorgang ausgegeben wird.
\end{dinglist}
\index{rm=\texttt{rm}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{rmdir}\label{rmdir}\index{rmdir=\texttt{rmdir}|(textbf}
Mit \texttt{rmdir} werden Verzeichnisse gelöscht. Das funktioniert nur, wenn
sie leer sind. Mit \texttt{-p} kann ein kompletter Verzeichnispfad gelöscht
werden, will sagen: Alle höher liegenden Verzeichnisse im angegebenen Pfad
werden gelöscht. Voraussetzung ist hier natürlich auch, daß die Verzeichnisse
nichts außer dem angegebenen Unterverzeichnis enthalten.
Angelegt werden Verzeichnisse mit \texttt{mkdir} (Abschnitt \ref{mkdir}),
nicht-leere Verzeichnisse können rekursiv mit \texttt{rm -r} (Abschnitt
\ref{rm}) gelöscht werden.
\index{rmdir=\texttt{rmdir}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{script}\label{script}\index{script=\texttt{script}|(textbf}
\begin{dinglist}{43}
\item Dies ist kein Standard-Kommando, es steht nicht auf allen Systemen zur
Verfügung.
\end{dinglist}
Dieses Kommando eignet sich vorzüglich für das Debuggen fertiger Skripte. Man
ruft es in Verbindung mit einem Dateinamen auf. Dieser Aufruf startet eine neue
Shell, in der man beliebige Kommandos ausführen kann. Wenn man fertig ist,
beendet man den script-Befehl durch die Eingabe von \texttt{exit},
\texttt{logout} oder Druck der Tastenkombination \Ovalbox{CTRL}+\Ovalbox{d}
(EOF).
Script schreibt alle Ein- und Ausgaben die an dem Terminal vorgenommen werden
in die angegebene Datei. So kann man auch interaktive Skripte relativ leicht
debuggen, da sowohl Ein- als auch Ausgaben in dem Logfile sichtbar sind.
\index{script=\texttt{script}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{sed}\label{sed}\index{sed=\texttt{sed}|(textbf}
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}
\begin{lstlisting}
# 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{lstlisting}
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.
\begin{dinglist}{43}
\item Die GNU-Version stellt noch ein paar Parameter zur Verfügung, die
Man-Page verrät näheres.
\end{dinglist}
\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:
\lstinline|s/Suchmuster/Ersatzmuster/g|
Im Ersatzmuster können auch Teile der Fundstelle wieder vorkommen, wenn sie
durch Klammern in einen Puffer kopiert werden:
\lstinline|s/Seite ([0-9]*) von ([0-9]*)/\1 aus \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}:
\lstinline|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.
\begin{lstlisting}
### 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{lstlisting}
\index{sed=\texttt{sed}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{seq}\label{seq}\index{seq=\texttt{seq}|(textbf}
\begin{dinglist}{43}
\item Dies ist kein Standard-Kommando, es steht nicht auf allen Systemen zur
Verfügung.
\end{dinglist}
Oft wird eine auf- oder absteigende Sequenz aufeinanderfolgender Zahlen
benötigt, beispielsweise um eine Schleife 100 mal zu durchlaufen. Es ist nicht
sehr performant bei jedem Schleifendurchlauf hochzuzählen und dann die
entstandene Zahl mit dem Limit zu vergleichen. Daher nimmt man an der Stelle
\texttt{seq} zur Hilfe, wenn es zur Verfügung steht.
Die zu zählenden Werte werden durch drei unterschiedliche Arten der
Parametrisierung definiert: Ein Aufruf in der Form \lstinline|seq 10| gibt die
Zahlen von 1 bis 10 aus. Mit \lstinline|seq 10 20| wird von 10 bis 20 gezählt,
und \lstinline|seq 20 -2 10| zählt in zweierschritten rückwärts von 20 nach 10.
Per default kommen die Werte zeilenweise, mit dem Parameter \texttt{-s} kann
aber auch ein anderes Trennzeichen definiert werden. Will man etwas numerieren
und später nach den Zahlen sortieren, ist es sinnvoll wenn `schmalere' Zahlen
mit führenden Nullen aufgefüllt werden. Das erreicht man mit dem Parameter
\texttt{-w}.
\index{seq=\texttt{seq}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{sleep}\label{sleep}\index{sleep=\texttt{sleep}|(textbf}
Das Kommando \texttt{sleep} veranlaßt die Shell, für eine angegebene Zeit zu
warten. Die Zeit wird dabei in Sekunden angegeben.
\begin{dinglist}{43}
\item In der GNU-Variante von \texttt{sleep} kann die Einheit der angegebenen
Zeitspanne durch Suffixe definiert werden: \lstinline|sleep 10s| schläft zehn
Sekunden, \lstinline|sleep 10m| zehn Minuten. Genauso werden Stunden (h) und
Tage (d) definiert.
Außerdem kann die GNU-Variante auch mit nicht-Integer Zeiten arbeiten:
\lstinline|sleep 0.5| schläft eine halbe Sekunde.
\end{dinglist}
\index{sleep=\texttt{sleep}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{sort}\label{sort}\index{sort=\texttt{sort}|(textbf}
Mit diesem Befehl wird wie der Name schon andeutet sortiert. Wenn kein
Dateiname als Parameter angegeben wird, liest \texttt{sort} von der
Standard-Eingabe. Geschrieben wird immer auf der Standard-Ausgabe.
Man kann sich vorstellen, daß ein solches Kommando recht flexibel sein muß,
daher stehen eine Menge Parameter zur Verfügung:
\LTXtable{\textwidth}{tab_kommandos_sort_parameter.tex}
Die Sortierung nach der Spalte (mit \texttt{-k}) ist etwas tricky. Die genaue
Syntax wird in der Man-Page mit \texttt{-k POS1[,POS2]} angegeben, das bedeutet
man muß einen Parameter angeben, man kann bei Bedarf einen zweiten angeben.
Bei der Sortierung wird dann der Bereich ab POS1, bzw. der Bereich zwischen
POS1 und POS2 berücksichtigt.
Dabei lautet die Syntax für POS \texttt{F[.C][OPTS]}. Dabei gibt F die
Feldnummer an (siehe Parameter \texttt{-t}). Wenn nicht nach dem Feld an sich
sortiert werden soll, kann C die Position des Zeichens innerhalb des Feldes
angeben. Und als ob das noch nicht kompliziert genug wäre, kann man dem ganzen
Konstrukt noch einen einbuchstabigen Parameter für die Sortier-Option mitgeben.
Wenn das angegebene Feld nicht existiert wird nach der ganzen Zeile sortiert.
OK, Beispiele:
\LTXtable{\textwidth}{tab_kommandos_sort_beispiele.tex}
Weitere Parameter verrät wie immer die Man-Page.
\index{sort=\texttt{sort}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{tail}\label{tail}\index{tail=\texttt{tail}|(textbf}
Der Befehl \texttt{tail} gibt die letzten zehn Zeilen einer Datei aus. Wenn
kein Dateiname (oder ein \texttt{-}) angegeben wird, liest \texttt{tail} von
der Standard-Eingabe. Man kann die Anzahl der ausgegebenen Zeilen mit dem
Parameter \texttt{-n} steuern.
Mit dem Parameter \texttt{-f} (follow) gibt \texttt{tail} neue Zeilen aus,
sobald sie an die Datei angehängt werden.
\begin{dinglist}{43}
\item Die GNU-Version kann auch das Ende mehrere Dateien ausgeben bzw.
verfolgen, wenn mehrere Namen angegeben werden.
\end{dinglist}
\index{tail=\texttt{tail}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{tee}\label{tee}\index{tee=\texttt{tee}|(textbf}
Dies ist praktisch ein T-Stück für Pipes. \texttt{tee} liest von seiner
Standard-Eingabe, und gibt alle Eingaben direkt auf der Standard-Ausgabe wieder
aus. Nebenbei werden die Ausgaben in eine oder mehrere Dateien geschrieben.
Wenn die Ausgabedateien schon existieren, werden sie überschrieben. Dieses
Verhalten kann mit dem Parameter \texttt{-a} geändert werden.
\index{tee=\texttt{tee}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{touch}\label{touch}\index{touch=\texttt{touch}|(textbf}
Mit diesem Kommando kann man einerseits Dateien anlegen wenn sie nicht
existieren, und andererseits die Änderungs- und Zugriffszeiten einer Datei
ändern. Ohne die Angabe weiterer Parameter wird die Datei erzeugt wenn sie
nicht existierte, bzw. in ihrer Änderungs- und Zugriffszeit auf die aktuelle
Zeit gesetzt.
Mit dem Parameter \texttt{-a} wird nur die Zugriffs-, mit \texttt{-m} nur die
Änderungszeit gesetzt. Mit \texttt{-c} kann die Erstellung einer neuen Datei
unterdrückt werden.
Die eingesetzte Zeit kann auch durch die Parameter \texttt{-t} bzw. \texttt{-d}
angegeben werden. Mit \texttt{-r} kann die Zeit der einer angegebenen
Referenzdatei angepaßt werden.
\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:
\begin{lstlisting}
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{lstlisting}
\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:
\lstinline|trap 'echo "`basename $0`: Ooops..." 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.
Ein weiterer nützlicher Einsatz für \texttt{trap} ist es, Signale zu
ignorieren. Das kann gewünscht sein, wenn eine Folge von Kommandos in einem
Skript auf keinen Fall unterbrochen werden darf. Um zu verhindern daß ein
\Ovalbox{CTRL}+\Ovalbox{C} des Benutzers das Skript beendet wird folgendes
Konstrukt eingesetzt:
\begin{lstlisting}
trap '' 2 # Signal 2 ist Ctrl-C, jetzt deaktiviert.
kommando1
kommando2
kommando3
trap 2 # Reaktiviert Ctrl-C
\end{lstlisting}
Vielleicht wäre es aber auch dem Benutzer gegenüber freundlicher, auf das
entkräftete Signal hinzuweisen:
\lstinline|trap 'echo "Ctrl-C ist ausser Kraft."' 2|
Eine Sonderbehandlung machen viele Shells, wenn als Signal \texttt{DEBUG}
angegeben wird. Dann wird nach jedem ausgeführten Kommando der Trap ausgelöst.
Dieses Feature wird wie der Name schon erahnen läßt zum Debuggen benutzt, ein
Beispiel findet sich in Abschnitt \ref{fehlersuche}.
\index{trap=\texttt{trap}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{type}\label{type}\index{type=\texttt{type}|(textbf}
Das in die Shell eingebaute Kommando \texttt{type} gibt Auskunft über die Art
eines ausführbaren Kommandos. So kann man herausfinden ob beim Absetzen des
Befehls ein externes Programm gestartet, eine Shell-Funktion ausgeführt oder
ein Alias benutzt wird.
Sucht man nur nach einer ausführbaren Datei, hilft \texttt{which} (Abschnitt
\ref{which}).
\index{type=\texttt{type}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\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.
\begin{dinglist}{43}
\item Einige der folgenden Parameter entsprechen nicht dem allgemeinen
Standard:
\end{dinglist}
\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{wait}\label{wait}\index{wait=\texttt{wait}|(textbf}
Das Kommando \texttt{wait} wird benutzt um auf die Beendigung eines Prozesses
zu warten. Als Parameter wird eine Prozeß-ID übergeben, \texttt{wait} läuft so
lange bis sich der Prozeß mit der angegebenen ID beendet hat.
Wenn keine Prozeß-ID angegeben wurde, wartet \texttt{wait} auf alle
Kind-Prozesse der aktuellen Shell.
Ein Beispiel für die Benutzung findet sich im Kapitel über Wachhunde (Abschnitt
\ref{wachhunde}).
\index{wait=\texttt{wait}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{wc}\label{wc}\index{wc=\texttt{wc}|(textbf}
Wie der Name schon suggeriert\footnote{Oder etwa nicht?!? ;-)} kann man mit
diesem Kommando Wörter zählen (word count). Gezählt wird entweder in einer
Datei, oder~--~wenn kein Dateiname angegeben wurde~--~in der Standardeingabe.
Weitaus häufiger wird aber der Parameter \texttt{-l} benutzt, mit dem sich die
Zeilen zählen lassen. Weiterhin kann man Bytes (\texttt{-c}) oder Zeichen
(\texttt{-m}) zählen lassen.
\begin{dinglist}{43}
\item Der Parameter \texttt{-L} gibt in der GNU-Version die Länge der längsten
enthaltenen Zeile aus.
\end{dinglist}
\index{wc=\texttt{wc}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{which}\label{which}\index{which=\texttt{which}|(textbf}
\begin{dinglist}{43}
\item Dies ist kein Standard-Kommando, es steht nicht auf allen Systemen zur
Verfügung.
\end{dinglist}
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.
Einen ähnlichen Zweck erfüllt auch \texttt{type} (Abschnitt \ref{type}).
\index{which=\texttt{which}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{who}\label{who}\index{who=\texttt{who}|(textbf}
Das Kommando \texttt{who} gibt eine Liste aller angemeldeten Benutzer, zusammen
mit deren aktueller Konsole und der Anmeldezeit aus.
\index{who=\texttt{who}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{xargs}\label{xargs}\index{xargs=\texttt{xargs}|(textbf}
Bisweilen kommt man in die Verlegenheit, versehentlich zu lange Einzeiler
geschrieben zu haben. Neben den Fällen, in denen der Tipp-Eifer überhand
genommen hat handelt es sich in aller Regel um Zeilen in der Art
\lstinline|grep 'text' $(find / -name \*.txt)|. Dieses Kommando sucht alle
Dateien mit der Endung txt, die im System vorhanden sind. Diese werden `in die
Kommandozeile eingebaut'. Wenn sehr viele Dateien gefunden werden, wird die
Zeile zu lang für die Shell\footnote{Die maximale Länge der Kommandozeile
unterscheidet sich von System zu System}.
Ein weiterer und in der Praxis mindestens ebenso sinnvoller Einsatzzweck ist
das Vermeiden von Schleifen. Das obige Problem ließe sich auch mit einer Zeile
in der folgende Form lösen:
\lstinline|find / -name \*.txt -exec grep 'text' {} \;|
Allerdings hätte das den Nachteil, daß für jede gefundene Datei ein neuer
\texttt{grep} gestartet werden muß. Das kostet Resourcen. Beide Probleme werden
durch eine Zeile in der folgenden Form umgangen:
\lstinline+find / -name \*.txt | xargs grep 'text'+
Dabei liest \texttt{xargs} aus der Standardeingabe die Parameter, die dann an
den \texttt{grep}-Aufruf angehängt werden. Sollten zu viele Dateien gefunden
werden, wird \texttt{grep} mehrfach aufgerufen, allerdings im Gegensatz zum
obigen Beispiel nicht einmal pro Fundstelle.
\begin{dinglist}{43}
\item Neben einigen anderen Parametern informiert die Manpage der GNU-Version
über die Option \texttt{-r}. Damit kann vermieden werden, daß \texttt{xargs}
das Kommando startet wenn keine Eingabe vorhanden ist. Bezogen auf das
angegebene Beispiel würde \texttt{grep} ohne Dateinamen gestartet, wenn
\texttt{find} nichts findet. Es würde auf Input von der Standardeingabe warten,
der aber wahrscheinlich nicht kommt. Das Skript würde hängen, wenn der
Parameter \texttt{-r} nicht angewandt würde.
\end{dinglist}
\index{xargs=\texttt{xargs}|)}