Wiki

Shell/Tipps und Tricks

Tipps und Tricks

Artikel für fortgeschrittene Anwender

Dieser Artikel erfordert mehr Erfahrung im Umgang mit Linux und ist daher nur für fortgeschrittene Benutzer gedacht.

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

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.

Tipps

Sinnlose Verwendung von cat

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" 

Sinnlose Verwendung von kill -9

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.

Sinnlose Verwendung von echo

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.

Sinnlose Verwendung von ls *

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 * 

Sinnlose Verwendung von wc -l

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.

Sinnlose Verwendung von grep | awk

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.

Sinnlose Verwendung von grep | sed

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.

Sinnlose Verwendung von Backticks

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.

Sinnlose Verwendung von test

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 

Regulärer Ausdruck und .*

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

Tricks

Leeren einer Datei

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

Kopieren mit tar und ssh

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

Passwort vergessen?