Home / LinuxUser / 2000 / 12 / Die Bash, das unbekannte Wesen

Newsletter abonnieren

Lies uns auf...

Folge LinuxCommunity auf Twitter

Top-Beiträge

Mandriva gibt Distribution in die Hände der Community
(268 Punkte bei 24 Stimmen)
Neues vom Systemd
(161 Punkte bei 4 Stimmen)
Mandriva in Nöten
(161 Punkte bei 4 Stimmen)

Heftarchiv

LinuxUser Heftarchiv

EasyLinux Heftarchiv

Ubuntu User Heftarchiv

Ubuntu User Heftarchiv

Partner-Links:

Shopping
Topsuche
 
Yatego Deutschlands größte Shoppingmall. 10000 Shops,
3.5 Mio Artikel. Alle Bestseller, Servertechnik und Technik Themenwelten.

Notebooks und Netzwerkhardware bei Mercateo günstig kaufen.
Internet Telefonie mit VoIP Telefonen von Gigaset
Das B2B Portal www.Linx.de informiert über Produkte und Dienstleistungen.
Günstige Digitalkameras finden Sie im Preisvergleich.

Jobs

Die Bash, das unbekannte Wesen

Von Befehlsfolgen und -blöcken zur Alias-Definition

Im letzten Teil dieser Serie wurden zwei Möglichkeiten erläutert, Befehle (via Pipes oder spezieller Shell-Operatoren) nacheinander als Befehlsfolge ausführen zu lassen. Grundsätzlich muß dabei unterschieden werden, ob die Befehle von der momentan aktiven Shell ausgeführt werden oder ob diese die Befehlsfolge an eine zweite Shell (die sogenannte Subshell) weiterreicht. Im ersten Fall können die Befehle Veränderungen an der ausführenden Shell vornehmen (etwa bestimmte Umgebungsvariablen verändern), im zweiten Fall ist dies nur indirekt möglich. Die Bash verwendet daher zwei unterschiedliche Syntax-Varianten für diese beiden Möglichkeiten:

$>  ( cd ;  find . -print > find.tmp ; less find.tmp ;)
$>  { cd ;  find . -print > find.tmp ; less find.tmp ;}

Die schließende Klammer wird bei dieser Schreibweise analog zu einem Befehlsnamen behandelt. Sie muß daher entweder nach einem Semikolon oder als erstes Wort in einer neuen Zeile stehen.

Die unterschiedlichen Effekte werden in diesem einfachen Beispiel deutlich: Im ersten Fall (Ausführen der Befehle in einer Subshell) ist am Ende der Bearbeitung das aktuelle Verzeichnis unverändert, im zweiten Fall dagegen nicht. Ähnlich verhält es sich mit den Umgebungsvariablen, Alias-Definitionen und Funktionen: In der ersten Variante gehen Änderungen beim Beenden der Befehlsfolge verloren, im zweiten Fall bleiben sie bestehen. Ein einfaches Beispiel dazu. Unter einem (Befehls-)Alias versteht man eine mit dem Bash-internen alias-Befehl definierte Abkürzung, die analog zu "normalen" Befehlen ausgeführt werden kann. Üblich sind beispielsweise folgende Alias-Definitionen:

$> alias al=alias
$> alias ll='ls -l'
$> alias df='df -h'
$> al cp='cp -iv '
$> al rm='rm -iv '
...

Im ersten Beispiel wird eine Abkürzung für den Alias-Befehl selbst definiert. Anstelle des ausgeschrieben Befehlsnamen alias kann nun al verwendet werden, wie dies in der vierten Zeile erfolgte. In der zweiten Zeile erfolgt die Deklaration eine der am häufigsten überhaupt verwendeten Alias-Definitionen. (Viele User glauben, ll sei ein eigenständiger Befehl. Das stimmt aber nicht.) Im dritten, vierten und fünften Beispiel werden Definitionen für bereits bestehende Befehlsnamen deklariert. Da die Bash Alias-Definitionen nicht rekursiv auswertet, ist dies zulässig. Der Effekt ist dabei folgender: Die Alias-Definition "maskiert" den eigentlichen Befehl, da die Bash immer zunächst prüft, ob für einen Befehlsnamen ein Alias besteht, bevor sie versucht, einen externen Befehl mit eben dem Namen auszuführen. Andere Shells verhalten sich übrigens analog. Ruft der Anwender also df auf, findet die Bash die Alias-Definition df -h (-h steht für ein durch Menschen lesbares Ausgabeformat "human readable") und führt den entsprechenden Befehl aus. Bei der Deklaration eines Alias muß die rechts vom Gleichheitszeichen stehende Definition in Hochkommata eingefaßt werden, wenn sie Leerzeichen enthält. Auf diese Weise werden, wie oben gezeigt, oft destruktive Befehle in ihrer Wirkung "entschärft". Diese Varianten von cp und rm fragen nach, bevor sie bestehende Dateien überschreiben.

Einige Anmerkungen zum Einsatz von Alias-Definitionen: Eine Alias-Definition wird von der Bash nur dann erkannt, wenn sie in der Befehlszeile dort auftritt, wo normalerweise Befehlsnamen erwartet werden, also beispielsweise als erstes Wort. Nach einem Befehl wie nohup erkennt die Bash eine Alias-Definition nicht als solche. Was ist nun aber, wenn, wie oben beschrieben, ein Befehl mit einer Alias-Definition maskiert wurde, aber seine ursprüngliche Form eingesetzt werden soll? Entweder geben Sie dann den Befehl mit seinem Pfad ein, oder Sie maskieren die Alias-Definition in der Befehlszeile mit einem Backslash-Zeichen:

$> /bin/df
$> \df

Alias-Definitionen weisen noch weitere Besonderheiten auf: Nur wenn sie mit einem Leerzeichen enden (wie oben in den Beispielen vier und fünf ) prüft die Bash, ob es sich bei dem auf ein Alias folgenden Wort ebenfalls um ein Alias handelt. Die Bash bearbeitet ("expandiert" heißt das im Fachjargon) den auf der rechten Seite einer Alias-Definition stehenden Code bei der Deklaration und nicht erst beim Ausführen des Alias. Manchmal ist es wichtig, dies zu berücksichtigen. In einer nicht-interaktiven Shell werden Alias-Definitionen gar nicht expandiert.

Alle aktuell deklarierten Alias-Definitionen werden angezeigt, wenn der alias-Befehl ohne weitere Argumente aufgerufen wird.

$> alias
...
alias search='grep *.tex -e'
alias searcha='agrep -B '
alias searchr='rgrep -nhB '
...

Eine Alias-Definition kann durch den unalias-Befehl gelöscht werden.

$> unalias searchr
$> searchr
bash: searchr: command not found

Die Bash kennt nur Alias-Definitionen ohne Parameter (Argumente). In den meisten Fällen ist dies ausreichend, da die wichtigsten Befehle bereits eine flexible Verwendung der Optionen und Argumente ermöglichen. Sogar der etwas störrische grep-Befehl kann in einem Alias eingesetzt werden, nutzt man diese Fähigkeiten aus:

$> grep Optionen -ef Muster Pfad 

$> alias search='grep *.tex -e'
$> search grep
bash-jobs.tex:Sogar der etwas störrische grep
-Befehl
bash-jobs.tex:$> alias search='grep *.tex -e'
bash-tips.tex:history grep
bash-tips.tex:alias hg='history | grep '     

search sucht in diesem Beispiel nur in den relevanten Tex(t)dateien nach dem angegeben Stichwort, alle anderen Files bleiben unberücksichtigt. Noch eleganter wird die Suche, wenn man spezielle grep-Varianten verwendet:

$> alias searcha='agrep -B '
$> alias searchr='rgrep -nhB '  

Mit agrep kann "unscharf" nach einen Stichwort gesucht werden, Treffer müssen dann nicht mehr exakt benannt und angegeben werden, sondern können mit Fehlern behaftet sein. Die Option -B (best) sucht automatisch die besten Ergebnisse heraus.

rgrep erlaubt die rekursive Suche und zusätzlich ein Hervorheben der Treffer:

$> searcha grtp *tex
agrep: 2 words match within 1 error; search for them? (y/n)
$> searchr grep
...
bash-jobs.tex:416:$> search grep   
...

Um auch für diese grep-Varianten die Zugriffe auf die relevanten Dateien einzuschränken, bedarf es eines etwas größeren Aufwands. Hierfür werden dann Funktionen eingesetzt.

Funktionen

Funktionen sind kleine Befehlsgruppen, die als funktionelle Einheit von der Bash temporär gespeichert werden. Ihre Deklaration wird mit dem Schlüsselwort function eingeleitet, gefolgt von zwei runden Klammern. Der eigentliche funktionelle Code steht zwischen geschweiften Klammern. Er endet mit einem Semikolon.

$> function Funktionsname
 () {" "Code
;" "}

Das Schlüsselwort function ist übrigens optional, es wird aber oft zum besseren Verständnis angegeben. Wichtig sind die durch " " gekennzeichneten Leerzeichen. Längere Funktionsdeklarationen erfolgen nicht mehr in einer Zeile, sondern in einer Blockform:

$> function  Funktionsname
 ()
>{
> zeilenweiser Code, oft eingerückt

>}

Bei dieser Eingabeform tritt nach der ersten Zeile ein spezieller Prompt auf: ">", der anzeigt, dass die Deklaration syntaktisch noch nicht abgeschlossen ist.

Ein wesentliches Merkmal von Funktionen sind ihre Parameter. Hier werden zunächst nur die sogenannten "Positionsparameter" (positional parameters) betrachtet. Ein Positionsparameter zeichnet sich nur durch seine Stellung in der Befehlszeile - also relativ zur Funktion - aus. Die Positionsparameter werden entsprechend von links nach rechts durchnumeriert. Auf jeden Positionsparameter kann durch seine Nummer Bezug genommen werden (er wird "referenziert"). Dazu wird diese Nummer nach einem Dollar-Zeichen angegeben:

$> kg () { echo $0 $1 $2 $1 $3 $4 $5 $6 $7 $8 $9 $10 $11; }
$>kg a b c d e f g h i j k l
bash a b a c d e f g h i a0 a1

Das Beispiel zeigt, dass die ersten zehn (0 bis 9) Parameter offenbar problemlos erkannt werden, wenn $0 auch eine besondere Bedeutung zukommt. Um elf oder mehr Parameter einer Funktion referenzieren zu können, müssen die Positionsnummern in geschweiften Klammern angegeben werden:

$> kg () { echo $0 $1 $2 $1 $3 $4 $5 $6 $7 $8 $9 ${10} ${11}; }
$>kg a b c d e f g h i j k l
bash a b a c d e f g h i k l

Mit den Positionsparametern können wir nun die Alias-Definitionen von oben verbessern:

$> function searcha () { agrep -B $1 *.tex; }

Für die rekursive Variante sieht es entsprechend aus:

$> function searchr () { rgrep -nhB $1 *.tex; }

Man kann das sogar noch schöner machen, wie in einer der nächsten Folgen gezeigt wird.

Neben dem Parameter $0, der immer das Nullte Argument einer Befehlszeile enthält - oder aber "bash" bei der Ausführung von Funktionen und Scripts -, gibt es noch eine Reihe weiterer spezieller Parameter. Diese werden hauptsächlich im Zusammenhang mit Funktionen und Scripts bedeutsam, stehen aber ebenfalls für eine interaktive Nutzung zur Verfügung.

Tabelle 1:Spezielle Parameter der Bash

Typ

Funktion

$* Dies wird durch den Inhalt aller in der Befehlszeile folgenden Positionsparameter ersetzt. Als Trennzeichen zwischen den Argumenten wird normalerweise ein Leerzeichen gesetzt. $* -> $1" "$2" "...
"$*" In dieser Form werden die Positionsparameter ohne Trennzeichen expandiert."$*"-> "$1$2..."
$@ Alle Positionsparameter ab dem ersten ($1) werden expandiert. Wird dieser Parameter in Hochkommata verwendet, expandiert die Bash die Positionsparameter einzeln in Hochkommata: "$@"-> "$1""$3""$2"...
$# Expandiert zu der dezimal angegeben Nummer von Positionsparametern.
$? Expandiert den Rückgabewert (Return Code) des zuvor ausgeführten Befehls in der Befehlszeile.
$$ Die Bash ersetzt dies durch die PID der aktuellen Shell. Bei einer Subshell wird die PID der aufrufenden Shell expandiert. Dieses Feature wird gern zum Erzeugen einmaliger Dateinamen verwendet.
$! PID des letzten Hintergrund-Jobs.
$0 Name des aktuellen Befehls oder Scripts. Bei Funktionen: bash.
$_ Absoluter Pfad des aktuellen Befehls oder Scripts beim Aufruf der Shell.

Alle zur Zeit deklarierten Funktionen können durch den eingebauten declare-Befehl mit der Option -f angezeigt werden:

$> declare -f
...
declare -f searcha ()
{
    agrep -B  $1 *tex
}
declare -f searchr ()
{
    rgrep -nHB $1 *tex
}
declare -f searchr ()
{
rgrep -nHB $1 *
}
...

Um nur die Funktionsnamen darzustellen, wird die Option -F verwendet.

ACHTUNG: Die Bash findet eine Alias-Definition immer vor einer gleichnamigen Funktionsdeklaration, sogar vor den eingebauten Shell-Befehlen. Achten Sie daher auf eindeutige Namen und verwenden Sie auch keine Schlüsselwörter und Befehlsnamen (etwa test, oder "y.", help, jobs, kill, etc.) als Funktions- und Aliasnamen, es sei denn, Sie wollen deren Funktion neu definieren. Mittels type kann festgestellt werden, wie die Bash ein Befehlswort interpretiert:

$> type searcha
searcha is a function
searcha ()
{
    agrep -B $1 *tex
}

Gelöscht werden Shell-Funktionen übrigens mit dem unset-Befehl:

$> unset Funktionsname

Funktionen können rekursiv verwendet werden, dürfen sich also in ihrer Deklaration selbst aufrufen.

$> kg(){ echo :kg:$SECONDS ; kg; }
$> kg
:kg:170540
:kg:170541
:kg:170541
:kg:170541
...

Hier zeigt sich, wie schnell Funktionen sein können, da sie direkt im Speicher vorgehalten werden.

$> function kg(){ let i=$i+1 ; echo $">i ; kg; }
$> kg
...
2761
2762
2763
2764
2765
...

Einem Freund empfehlen    Druckansicht Bookmark and Share
Kommentare

Hits
Wertung: 0 Punkte (0 Stimmen)

Schlecht Gut

Infos zur Publikation

Infos zur Publikation

LinuxUser 06/2012

Aktuelle Ausgabe kaufen:

Heft bestellen Heft als PDF kaufen

LinuxUser erscheint monatlich und kostet in der Nomedia-Ausgabe EUR 5,50 und mit DVD EUR 8,50. Weitere Informationen zum Heft finden Sie auf der LinuxUser-Homepage.

Im LinuxUser-Probeabo erhalten Sie drei Ausgaben für 3 Euro. Das Jahresabo (ab EUR 56,10) können Sie im LNM-Shop bestellen.

Tipp der Woche

Adobe AIR
Adobe-AIR-Programme installieren und (manuell) starten
Tim Schürmann, 14.05.2012 13:09, 0 Kommentare

Es gibt sie noch: neue Anwendungen, die Adobes Integrated Runtime voraussetzen. Aktuellstes und vermutlich auch größtes Beispiel ist das Adventure Botanicula

Aktuelle Fragen

gibt es ein Kommandozeilen Tool, um ein X11-Fenster in ein Anderes einzubetten?
GoaSkin , 21.05.2012 16:44, 0 Antworten
Das XEmbed-Protokoll ist u.A. dazu gedacht, dass man eine X11-Anwendung in eine andere wie ein Wi...
Apache2, Options -Indexes geht nicht
no no, 12.05.2012 19:01, 8 Antworten
Habe in apache2.conf folgendes stehen: Options -Indexes ...
LInux auf Dell LS H500
Andreas Endresl, 09.05.2012 08:54, 2 Antworten
Habe einen alten Dell Latitude LS H500 nur mit ext. Floppy und CD es geht nur immer eines von den...
Datenwiederherstellung unter Ubuntu 12.04 mit "Simple Backup" nach Umzug von Linux Mint
Christian Lottmann, 07.05.2012 13:33, 0 Antworten
Vor dem Umzug auf Ubuntu 12.04 habe ich unter Linux MInt mit "Simple Backup" voll (15.4.2012) und...
DKMS für den propritären NVIDIA-Treiber
Commander Data, 26.04.2012 22:02, 2 Antworten
Hallo an die Gemeinde. Ich habe hier ein interessantes Stück openSuSE gefunden. http://forums.op...