Dieser Artikel erfordert mehr Erfahrung im Umgang mit Linux und ist daher nur für fortgeschrittene Benutzer gedacht.
Dieser Artikel soll ein wenig Hilfe geben, "besser" mit der Shell umzugehen.
Insbesondere wird ein Augenmerk darauf gelegt, übersichtlich zu bleiben und dabei möglichst wenige Prozesse zu verwenden. In Zeiten von Gigahertz-Prozessoren wirkt das vielleicht ein wenig anachronistisch, wenn allerdings ein "schlechtes" Konstrukt in einem Skript einige tausend oder gar millionen Male ausgeführt wird, können nicht unerhebliche Laufzeiten entstehen.
Weiterhin soll hier der eine oder andere nicht offensichtliche Trick zu finden sein.
Die Shell und die Standard-Unix-Hilfsprogramme wie beispielsweise grep, sed, awk sind eine mächtige Kombination, die das Computer-Leben erleichtern kann.
Das Programm cat wurde dazu geschrieben, um mehrere Dateien aneinanderzuhängen (concatenate in englisch). Es wird aber häufig dazu verwendet, den Inhalt einer Datei an ein anderes Programm zu übergeben.
Beispielsweise (falsch):
cat dateiname | grep "suchbegriff"
Richtig stattdessen:
grep "suchbegriff" dateiname
Das Argument "ich verfeinere den Suchbegriff, da behindert mich der Dateiname am Ende der Zeile nur" lässt sich dadurch entkräften, dass die Ein- und Ausgabeumleitungen mit "<" und ">" an beliebiger Stelle innerhalb der Zeile stehen dürfen.
Möglich ist somit auch
<dateiname grep "suchbegriff"
kill -9 bricht einen Prozess ohne Rücksicht auf Tochterprozesse ab. Das sollte nur dann in Erwägung gezogen werden, wenn ein kill -15 nicht mehr funktioniert.
kill -15 sendet eine "Beenden-Aufforderung" an den Prozess, damit bekommt er die Gelegenheit, offene Dateien zu schließen, die Tochterprozesse sauber zu beenden, Socket-Verbindungen zu beenden, temporäre Dateien zu löschen, ...
Erst wenn das nicht funktioniert, weil der Prozess hängt, ist ein kill -9 angebracht.
Das ist ein Spezialfall der sinnlosen Verwendung von Backticks (weiter unten).
Häufig ist folgende (falsche) Verwendung zu sehen:
Kommando `echo $variable`
Die Backticks öffnen eine separate Shell um das Kommando echo auszuführen.
Eine Shell interpretiert jede Kommandozeile zweistufig. In der ersten Stufe werden alle Variablen durch ihre Inhalte ersetzt, es wird eine Dateinamenersetzung und eine Kommandoersetzung durchgeführt. In der zweiten Stufe wird die daraus entstandene Kommandozeile ausgeführt.
Damit ist folgendes der richtige Weg:
Kommando $variable
In der ersten Stufe wird $variable durch dessen Inhalt ersetzt. In der zweiten Stufe wird die daraus entstandene Zeile ausgeführt.
Ein weiterer Spezialfall der sinnlosen Verwendung von Backticks ist der Einsatz von ls *.
Falsch:
for i in `ls *` do cat $i done
Wie im vorigen Abschnitt beschrieben, durchläuft jedes Shellkommando einen zweistufigen Interpretationsprozess. In der ersten Stufe wird eine Dateinamenersetzung durchgeführt, so dass das folgende besser ist.
for i in * do cat $i done
Jedes Vorkommen von * wird durch die Dateien ersetzt, die auf * passen. Da aber cat auch die Angabe mehrerer Dateien erlaubt, ist das folgende die beste Lösung:
cat *
Sehr häufig sind Konstrukte anzutreffen, in denen etwas gesucht und die Anzahl der Zeilen mit Treffern ausgegeben wird.
grep "suchbegriff" dateiname | wc -l
Das Programm grep selber hat allerdings die Option die Anzahl der Trefferzeilen auszugeben.
Daher ist
grep -c "suchbegriff" dateiname
die bessere Lösung.
Überhaupt lohnt sich ein Blick in die Kommandozeilenoptionen von grep.
awk wird von vielen überwiegend dazu verwendet, Spalten einer Logdatei (oder einer anderen Ausgabe) auszugeben.
Um die Zeilen zu finden, aus denen awk die Spalten ausgeben soll, wird meistens grep vorgeschaltet, so dass ein solches Konstrukt dabei entsteht:
grep "suchbegriff" dateiname | awk '{ print $2 }' Das gibt die zweite Spalte von Zeilen aus, die den suchbegriff enthalten.
Da awk auch suchen kann, ist es aber sinnvoller
awk '/suchbegriff/ { print $2 }' dateiname zu verwenden.
Häufig werden aus Dateien mit grep die Zeilen gesucht, in denen man etwas mit sed suchen und ersetzen möchte.
grep "suchbegriff" dateiname | sed 's/anderesuche/ersetzungstext/'
Da sed auch suchen kann, ist es aber sinnvoller
sed '/suchbegriff/s/anderesuche/ersetzungstext/' dateiname
zu verwenden.
Ein Skript, in dem häufig Backticks (oder das Pendant $( ... )) vorkommen, kann man sehr häufig optimieren.
for i in `cat dateiname` ; do ... done
Lässt sich optimieren nach
while read i ; do ... done < dateiname
Wenn statt cat dateiname ein komplexerer Befehl benutzt wird, ist es besser statt
for i in `komplexerer Befehl` ; do ... done
komplexerer Befehl | while read i ; do ... done
zu schreiben.
Es ist überflüssig
kommando if [ $? -ne 0 ] ; then echo "fehler" exit 255 fi
zu schreiben, wenn doch auch
if ! kommando ; then echo "fehler" exit 255 fi
funktioniert.
In dem Zusammenhang noch ein gerne gemachter "Fehler" mit grep.
grep "suchbegriff" datei > /dev/null if [ $? -ne 0 ] ; then
ist genauso wenig optimal wie
if grep "suchbegriff" datei > /dev/null ; then
weil es dafür "-q", welches die Suche beim ersten Treffer abbricht, gibt.
if grep -q "suchbegriff" datei ; then
.* könnte man auch umschreiben mit "alles oder nichts". Das ist der reguläre Ausdruck, der immer wahr ist.
. ist das Jokerzeichen, steht also für irgendein Zeichen.
* steht für kein Mal oder beliebig oft.
Ein beliebiges Zeichen, kein Mal oder beliebig oft, trifft auf alles zu.
Wenn statt des * ein + verwendet wird, ergibt es Sinn. Das + steht für wenigstens ein Mal.
Man kann eine Datei namens "dateiname" mittels
> dateiname
leeren.
Vorteil: Der Dateihandle ändert sich nicht. Was bedeutet, dass ein Programm, dass die Datei als Logdatei benutzt, dort weiterhin hineinschreiben kann, ohne neu gestartet werden zu müssen (Beispiel Serverdienste und deren Logdateien).
Wenig bekannt ist, dass SSH auch Standardeingabe und Standardausgabe verwenden kann. Damit ist es möglich via
tar cf - <dateien oder verzeichnisse> | ssh <user>@<zielhost> "cd <zielverzeichnis> && tar xf -"
<dateien oder verzeichnisse> vom aktuellen Rechner ins <zielverzeichnis> auf dem <zielhost> als <user> zu kopieren. Das "-"-Zeichen bei tar cf sorgt dafür, dass tar seinen Datenstrom in Richtung Standardausgabe schickt, die wird mittels "|" umgelenkt auf den ssh-Befehl, in dem wiederum tar xf - die Daten von der Standardeingabe liest.
Das ist noch nichts spektakuläres. Das kann scp auch. Allerdings überträgt scp - auch mit der Option -p - keine Dateieigentümerschaften und löst symbolische Links auf, anstatt sie 1:1 zu übertragen. Deswegen ist scp für das exakte Klonen eines Verzeichnisbaums ungeeignet.
Zusammen mit dem Paket buffer (aus universe) kann man den Datentransfer außerdem gerade bei sehr vielen kleinen Dateien beschleunigen.
tar cf - <dateien oder verzeichnisse> | buffer -m 10m | ssh <user>@<zielhost> "cd <zielverzeichnis> && tar xf -"
Das buffer -m 10m sorgt dafür, dass ein Puffer von 10 Megabytes eingerichtet wird.
Diese Revision wurde am 2. April 2009 um 19:08 Uhr
von Dolorias erstellt.
Dieser Seite wurden folgende Begriffe zugeordnet:
Programmierung, Shell
2004 – 2010 ubuntuusers.de • Einige Rechte vorbehalten