Die Bash, das unbekannte Wesen

Aus LinuxUser 12/2000

Die Bash, das unbekannte Wesen

Jobs

Es gibt neben den Pipes noch verschiedene andere Verfahren, um mehrere Shell-Befehle miteinander zu verbinden bzw. sie parallel auszuführen. Die wichtigsten Methoden werden in dieser Folge der Bash-Serie vorgestellt.

Ein Überblick

Wie im letzten Teil beschrieben, können mit Befehlsfolgen viele Aufgaben unter Linux einfach und schnell via Pipes erledigt werden. Die Bash als Multitasking-Programm ist aber ebenfalls in der Lage, gleichzeitig mehrere Prozesse, sogenannte “Hintergrund-Jobs”, zu verwalten und zu kontrollieren.

Ein Hintergrundjob entsteht durch das “Ablösen” eines Prozesses vom kontrollierenden Terminal. Normalerweise können einem beliebigen Programm von dem Terminal aus, von dem es gestartet wurde, Signale zugesendet werden. Ein einfaches Beispiel: Ein lange andauernder sort-Befehl kann – wie die meisten anderen Befehle – durch [Strg]-[c] beendet werden. Viele Programme können andere Signale empfangen und interpretieren. So wird etwa der init-Prozeß (oder auch xinit) durch das SIGHUP-Signal (HUP: hang up “auflegen”) veranlaßt, seine Konfigurationsdatei erneut einzulesen. Viele weitere Programme (aber nicht unbedingt alle) reagieren so auf dieses Signal. Es wird mittels des kill-Befehls an die entsprechenden Prozesse gesendet.

#>kill -1 1

In diesem Beispiel wird durch -1 das SIGHUP-Signal ausgewählt und mit 1 die PID (Process Idenfitfication Number) des Zielprozesses (hier: init) festgelegt. Normalerweise muß dessen PID zunächst ermittelt werden. Entweder wird dafür der ps-Befehl eingesetzt, oder pidof (Process ID of running programm):


 $> #>ps
  PID TTY TIME CMD
...
  329 tty2 00:00:00 bash
  346 tty2 00:00:00 startx
  347 tty2 00:00:00 tee
  356 tty2 00:00:00 xinit
  360 tty2 00:02:20 kwm
  663 tty2 00:00:22 kfm
  666 tty2 00:00:05 krootwm
  671 tty2 00:01:22 kpanel
  672 tty2 00:00:07 kbgndwm
  677 tty2 00:01:00 kvt
...

In der ersten Spalte dieser Ausgabe stehen die PIDs. Dem Systemverwalter steht mit pidof ein komfortabler (aber wesentlich gefährlicherer) Befehl zur Ermittlung von PIDs zur Verfügung:

#pidof xinit
356

pidof kann gewöhnlich nur vom Systemverwalter ausgeführt werden, da es sich bei dem Programm um einen Link auf /sbin/killall5 handelt.

Nachdem die PID des gewünschten Prozesses bekannt ist, kann diesem das erforderliche Signal zugesandt werden. Dazu wird der kill-Befehl eingesetzt. Ein kurzer Blick auf die Online-Hilfe zeigt die Syntax:


$> help kill
kill: kill [-s sigspec | -n signum | -sigspec] [pid | job]... or
kill -l [sigspec]
    Send the processes named by PID (or JOB) the signal SIGSPEC.  
    If SIGSPEC is not present, then SIGTERM is assumed. ...

Hier sind einige Anmerkungen zu kill angebracht. Zunächst einmal, warum wird die Online-Hilfe mit help aufgerufen und nicht wie üblich mit der Option -help? Die Bash verfügt über einen internen kill-Befehl, für den die Online-Hilfe (wie für alle eingebauten Befehle, mehr dazu im nächsten Teil dieser Reihe) durch help aktiviert wird. Dieser eingebaute Befehl wird immer verwendet, wenn nicht die externe Befehlsvariante durch Angabe des Pfades (üblicherweise /bin/kill) explizit aufgerufen wird. Für unsere Zwecke ist der eingebaute kill-Befehl aber genau richtig. Als nächstes betrachten wir die Optionen von kill: -s erwartet als Argument den Namen eines Signals, voreingestellt ist SIGTERM, das zum unbedingten Programmende führt und durch [Strg]-[C] erzeugt wird. Durch -n kann die Angabe der Signale numerisch erfolgen, ebenso wie durch ein einfaches vorangestelltes Minuszeichen. Diese Eigenschaft wurde in dem init-Beispiel oben ausgenutzt. Als nächstes Argument muß entweder die PID des Zielprozesses angegeben werden oder eine Job-Nummer. Zu den Jobs kommen wir gleich. Alternativ kann kill mit der Option -l (list signals) aufgerufen werden:


 
$> kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     17) SIGCHLD
18) SIGCONT     19) SIGSTOP     20) SIGTSTP     21) SIGTTIN
22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO
30) SIGPWR      31) SIGSYS  

Diese Option bewirkt, dass kill die verfügbaren Signale sowohl numerisch als auch mit ihren Namen ausgibt, so wie der Befehl sie als Argumente der Option -s versteht. Das erste Beispiel hätte also wie folgt geschrieben werden können:


#kill -s SIGHUP 1

Die tatsächliche Wirkung eines Signals hängt von dem empfangenden Programm ab. Ihre üblichen Funktionen werden in [2] beschrieben. Nur wenige Programme reagieren auf alle Signale, und wenn sie eines interpretieren, ist die Funktion nicht immer genau vorhersehbar. Hier hilft ein Blick in die Dokumentation des Programms. Einige Signale haben aber immer dieselben Funktionen: Mit SIGTERM wird ein Programm “zwangsweise, aber vorschriftsgemäß” beendet, durch SIGKILL wird es “gnadenlos abgewürgt”. Im ersten Fall kann das Programm noch aufräumen, also beispielsweise temporäre Dateien löschen, Verbindungen zu anderen Prozessen ordnungsgemäß beenden und ähnliches. Dazu bleibt im zweiten Fall keine Gelegenheit mehr, diese Variante (kill -9 ...) sollte also wirklich nur in Ausnahmesituationen angewendet werden. Die Signal-Voreinstellung mit SIGTERM hat übrigens zu dem Programmnamen “kill” geführt.

Mit den Signalen bietet die Bash eine einfache Möglichkeit, laufende Prozesse “von außen” zu beeinflussen. So richtig spannend wird dies, wenn Hintergrund-Jobs mit ins Spiel kommen.

Befehle im Hintergrund ausführen

Bisher wurden Befehle auf der Bash immer direkt ausgeführt. Die Shell hat das entsprechende Programm geladen, die Ein- und Ausgabekanäle mit dem Terminal verbunden und sich um die Ressourcen beim Programmablauf gekümmert. Die Konsequenz daraus war, das die Shell bis zum Ende des auszuführenden Programms gesperrt war. Heute ist dies nicht mehr ganz so schlimm, kann der Anwender bei Bedarf doch einfach ein neues virtuelles Terminal unter X öffnen und dort weiterarbeiten. Neben einem völlig unnötigen Ressourcenverbrauch führt dies aber schnell zu einem wenig übersichtlichen Destop. Daher bieten alle modernen Shells die Möglichkeit, laufende Programme “im Hintergrund ausführen zu lassen”.

Die einfachste Art, einen Befehl im Hintergrund auszuführen, bietet die Bash mit dem “&”-Operator (Ampersand-Zeichen). Befehlszeilen, die mit diesem Zeichen enden, werden automatisch im Hintergrund ausgeführt. Dabei verliert das Programm teilweise die Verbindung zu dem Terminal, auf dem es gestartet wurde. Es kann keine Eingaben (oder Signale) mehr von dort empfangen. Ausgaben kann das Programm aber noch an das Terminal senden, wie folgendes Beispiel zeigt:


 $>
$> ls -R /&
[1] 31886
$> ls -l
/:
bin   cdrom  etc     home  lost+found  opt   root  tmp  var
boot  dev    floppy  lib   mnt         proc  sbin  usr
insgesamt 45
-rw-r-r-  1 work  users   10030 Oct  2 17:54 #bash-jobs.tex#
-rw-r-r-  1 work  users      48 Oct  2 17:36 bash-jobs.aux
-rw-r-r-  1 work  users   10136 Oct  2 17:36 bash-jobs.dvi
-rw-r-r-  1 work  users    8517 Oct  2 17:36 bash-jobs.log
-rw-r-r-  1 work  users   10021 Oct  2 17:45 bash-jobs.tex
...

Die ersten drei Zeilen stammen dabei von ls -R, die folgenden wurden durch ls -l erzeugt. Im Hintergrund laufende Prozesse werden als “Jobs” bezeichnet.

Eine andere Möglichkeit für Hintergrund-Jobs ist die folgende: Auch bereits laufende Programme können “in den Hintergrund geschickt” werden, um das Terminal für andere Aufgaben zu verwenden. Ein typisches Beispiel sieht folgendermaßen aus: Sie starten ein Programm und merken dann, dass einige Einstellungen falsch oder unvollständig waren. In vielen Fällen können Sie nun das Programm einfach durch die Tastenkombination [Strg]+[z] anhalten, die notwendigen Dateien kopieren (oder was immer erforderlich ist) und das Programm weiter laufen lassen. Soll es das Terminal, von dem aus es gestartet wurde, wieder freigeben, werden Sie es durch bg (“background”)im Hintergrund laufen lassen. Mit fg (“foreground”) können derartig laufende Programme erneut in den Vordergrund geholt (mit dem Terminal verbunden) werden. Eine Übersicht aller im Hintergrund einer Shell laufenden Programme gibt Ihnen der Befehl jobs:


$> jobs
[2]-  Running   xv &
[3]+  Stopped   man bash

Die Ausgabe von jobs kann durch einige Optionen modifiziert werden: -l bewirkt, dass der Befehl auch die PIDs der laufenden Prozesse anzeigt, durch -p werden nur diese dargestellt. Mit -n zeigt jobs nur solche Jobs an, deren Status sich seit dem letzten Aufruf verändert haben, die also von “schlafend” zu “laufend” gewechselt haben, o. ä. -r zeigt nur die aktiven (laufenden: “running”) Jobs an, -s nur die schlafenden. Ein beliebiger Befehl kann durch jobs mit den Jobnummern als Argument ausgeführt werden:


$> jobs -x kill %2
[2]- Exit 1       xv  

In diesem Beispiel wird die Jobnummer (%2) anstelle der PID an den kill-Befehl übergeben. Oft ist diese Methode übersichtlicher als die Verwendung der PIDs, zumal nur auf die Jobs der lokalen Shell zugegriffen werden kann und alle anderen Prozesse so vor Fehlern geschützt sind. Der eingebaute kill-Befehl der Bash verfügt übrigens per sè über die Fähigkeit, Prozesse via Jobnummern identifizieren zu können.

Eine besondere Situation tritt bei lange laufenden Jobs auf. Wenn sich der User abmeldet, werden normalerweise alle von ihm initiierten Jobs beendet, indem die Shell das Signal SIGHUP an diese Prozesse sendet. (Schlafende Prozesse werden zuvor mittels SIGCONT geweckt.) Angenommen, Sie rechnen ein kompliziertes Modell, vielleicht rendert PovRay Ihnen gerade eine Grafik, dann müssen Sie aber nicht bis zum Ende der Berechnungen eingeloggt bleiben (und Ihr Terminal bewachen). Es gibt zwei Möglichkeiten, lange laufende Jobs vor dem Beenden beim Logout zu schützen.

Variante eins setzt auf einen speziellen Befehl mit dem Namen nohup (für “no Hang Up”). Diesem Befehl wird eine Befehlszeile als Argument übergeben, die nach dem Logout weiter ausgeführt werden soll. nohup fängt dann SIGHUP-Signale (und auch SIGTERM, SIGINT sowie SIGQUIT) ab, die die Shell an das in der Befehlszeile enthaltene Programm schickt. Als erstes versucht der Befehl aber eine Ausgabedatei für mögliche Meldungen und Ausgaben durch die in der Befehlszeile enthaltenen Befehle zu öffnen. Normalerweise wird dazu die Datei nohup.out im aktuellen Verzeichnis erzeugt. Gelingt dies nicht, versucht nohup diese Datei im Home-Verzeichnis des Anwenders anzulegen. Ist auch dies nicht möglich, terminiert der Befehl ohne die Befehlszeile auszuführen. Die GNU-Variante dieses Befehls läßt sich leicht modifizieren, da es sich um ein kleines Script handelt.


$> nohup lange-laufendes-Programm &
$> logout

Zwei Anmerkungen zu nohup: Der Befehl bewirkt nicht, dass die übergebene Befehlszeile im Hintergrund ausgeführt wird, daher sollte die Befehlszeile mit dem Ampersand-Zeichen abgeschlossen werden. Auch sollte berücksichtigt werden, dass nohup die übergebene Befehlszeile mit einer (etwas) verringerten Priorität ausführt (Nice-Wert von “5”), sich die Bearbeitungszeit also verlängert, wenn hier nicht mit renice gegengesteuert wird.

Variante zwei nutzt ein spezielles Feature der Bash. Mit dem eingebauten Befehl disown können gezielt einzelne Hintergrund-Jobs in der Job-Tabelle so markiert (versteckt: hide) werden, dass sie für bestimmte Signale nicht mehr erreichbar sind. Durch die Option -h wird verhindert, dass dem entsprechenden Job ein SIGHUP-Signal gesendet werden kann.

Eine weitere Besonderheit ist beim Einsatz von Jobs zu beachten. Mit der unter Linux üblichen GNU-Variante des Terminal-Konfigurationsbefehls stty können durch die Option tostop alle Hintergrund-Jobs automatisch angehalten werden, wenn Sie versuchen, auf ein Terminal zu schreiben. (Solange Sie diesen Versuch nicht unternehmen, laufen die Prozesse ganz normal.)


$> stty tostop   aktiviert die Funktion
$> stty -tostop  deaktiviert sie wieder

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

LinuxUser 12/2000 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben