Viele Linux-Einsteiger stolpern bei den ersten Schritten im Terminal über den Punkt, dass man beim Aufruf von eigenen Skripten oder Programmen ein
Die Zeichenkombination ./ – also ein Punkt (“dot”) gefolgt von einem Schrägstrich (“slash”) – dürfte den meisten erfahrenen Linux-Anwendern als Präfix vor Befehlen bekannt sein. Besonders Linux-Einsteiger stolpern jedoch über diese Kombination, wenn es darum geht, ein Programm oder Skript auszuführen, das im aktuellen Verzeichnis der Shell liegt. Doch was steckt hinter dieser kryptischen Zeichenfolge, und warum müssen Sie sie eigentlich immer eingeben? Wir bringen mit diesem Artikel ein wenig Licht in die Sache.
Bevor wir allerdings zum Thema ./ kommen, müssen wir erst einmal ein wenig ausholen und einige Worte zur Linux-Shell und der Umgebungsvariable $PATH verlieren. Eine Shell stellt im Grunde ein relativ einfaches Programm dar, das eine textbasierte Benutzerschnittstelle lädt, in die der Benutzer seine Befehle eingibt, die dann letztendlich von der Shell ausgeführt werden. Neben der weitverbreiteten Bourne-Again-Shell Bash gibt es noch weitere Shells, wie etwa die komfortable Z-Shell Zsh – wir konzentrieren uns jedoch auf die Bash.
Beim Aufruf eines Befehls führt die Shell entweder ein fest eingebautes Kommando aus oder startet ein ausführbares Programm oder Skript, das sich irgendwo auf der Festplatte befindet. Zur ersten Gruppe gehören Befehle wie etwa cd, echo, kill oder alias. Sie sind fest in der Shell integriert, es gibt also nirgends eine Programmdatei zum Kommando. Im Gegensatz dazu stehen ausführbare Programme wie mv, less oder große Anwendungen wie gedit oder firefox, deren ausführbare Programmdateien meist im Verzeichnis /usr/bin/ liegen.
Geben Sie nun einen Befehl in die Shell ein, so durchsucht diese erst einmal die in der Shell-Variablen $PATH (Listing 1) aufgeführten Verzeichnisse nach dem eingegebenen Programm. Dabei kommt die Reihenfolge der Einträge zum Tragen. Sucht die Shell etwa anhand des Pfads aus Listing 1 nach dem Programm foo und spürt die entsprechende Datei in /usr/local/bin auf, dann führt sie /usr/local/bin/foo aus – selbst wenn es in /usr/bin/ ebenfalls ein Programm namens foo gäbe. Erst wenn die Suche im Pfad keinen Treffer liefert, führt die Shell ein eventuell passendes internes Kommando aus. Diesen Punkt gilt es, im Hinterkopf zu behalten.
Listing 1
$ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
Skripte ausführen
Nun schließen wir den Bogen zu ./ und beschäftigen uns mit dem Ausführen von Programmen und Skripten. Sie möchten also ein selbst geschriebenes Skript oder ein als Binary heruntergeladenes Programm aufrufen. Als Beispiel wählen wir ein Skript namens beispiel.sh im Verzeichnis tmp/ innerhalb Ihres Home-Verzeichnisses. Ohne große Überlegung wechseln wir mit cd in das Verzeichnis, machen das Skript ausführbar und prüfen mit ls, ob auch das Executable-Bit gesetzt wurde. Rufen Sie dann das Skript auf, passiert – nichts: Die Shell meldet command not found (Listing 2).
Listing 2
tux@computer:~$ cd tmp tux@computer:~/tmp$ chmod +x beispiel.sh tux@computer:~/tmp$ ls -al beispiel.sh -rwxr-xr-x 1 tux tux 61 2010-02-03 15:28 beispiel.sh tux@computer:~/tmp$ cat /beispiel.sh #!/bin/bash echo "Dies ist nur ein Beispiel für ein Skript." tux@computer:~/tmp$ beispiel.sh beispiel.sh: command not found
Die Shell meint also, das Skript nicht finden zu können – und das, obwohl wir uns im richtigen Verzeichnis befinden, das Skript dort auch tatsächlich liegt und zudem auch die Rechte stimmen. Eigentlich müsste die Shell unser Beispiel-Skript also ausführen – warum klappt das nicht? Des Rätsels Lösung liegt im vorhin beschriebenen $PATH: Sehen Sie sich noch einmal das Listing 1 an.
Die Shell stellt fest, dass sich in den in $PATH aufgeführten Verzeichnissen nirgendwo ein Programm namens beispiel.sh befindet. Auch die Suche nach einem internen Kommando mit diesem Namen liefert keinen Treffer. Als Letztes müsste die Shell also im aktuellen Verzeichnis nachsehen, doch das tut sie nicht. Stattdessen quittiert sie den Programmaufruf direkt nach der Suche im Pfad und in den internen Kommandos.
Um also unser Beispielskript beispiel.sh auszuführen, müssten Sie dieses entweder in ein Verzeichnis innerhalb von $PATH kopieren oder dem Aufruf auch den vollständigen Pfad zur Datei mitgeben. Drei mögliche, funktional identische Pfadangaben dazu zeigt Listing 3.
Listing 3
tux@computer:~/tmp$ /home/tux/tmp/beispiel.sh Dies ist nur ein Beispiel für ein Skript. tux@computer:~/tmp$ $HOME/tmp/beispiel.sh Dies ist nur ein Beispiel für ein Skript. tux@computer:~/tmp$ ~/tmp/beispiel.sh Dies ist nur ein Beispiel für ein Skript.
Wie Sie sehen, gibt es also mehrere Varianten, um den Pfad zu einer Datei anzugeben, wie etwa die Tilde ~ mit dem Pfad zum Home-Verzeichnis des aktuellen Benutzers. Die kürzeste Variante, um einen Pfad zum aktuellen Verzeichnis anzugeben, ist ein schlichter Punkt. Zwei Punkte hintereinander verweisen dann auf das Elternverzeichnis – das Prinzip kennen Sie sicher schon vom Aufruf cd ... Stecken Sie in der Shell nun bereits im Verzeichnis mit der ausführbaren Datei, genügt zum Start der beispiel.sh eben ein Dotslash ./ als Pfadangabe (Listing 4).
Listing 4
tux@computer:~$ cd tmp tux@computer:~/tmp$ ./beispiel.sh Dies ist nur ein Beispiel für ein Skript.
Sicherheit vor Bequemlichkeit
Sie stellen sich nun mit Sicherheit die Frage, warum das aktuelle Verzeichnis nicht von Haus aus im Pfad steckt – bei der Microsoft-Eingabeaufforderung klappt das ja auch. Dem Anwender würde viel Tipparbeit und vor allen Dingen besonders beim Linux-Einstieg Verwirrung erspart bleiben. Der Grund dafür ist simpel: Es geht um Sicherheit.
Gehen wir von dem nicht unwahrscheinlichen Fall aus, dass Sie von einer Person einen USB-Stick mit einer ganzen Reihe von Dateien erhalten haben, die Sie per Shell-Kommando auf Ihr System kopieren möchten. Die Aufgabe sollte selbst Linux-Einsteiger vor keine großen Herausforderungen stellen: Sie wechseln mit cd ins entsprechende Verzeichnis auf dem USB-Stick und verschieben dann mit dem Kommando mv die Daten auf die Festplatte (Listing 5).
Listing 5
tux@computer:~$ cd /media/usb-stick tux@computer:/media/usb-stick$ mv beispiel.* /wohin/auch/immer
Was aber, wenn auf dem USB-Stick ebenfalls eine Datei mv läge, mit dem Inhalt aus Listing 6? Würde die Shell das aktuelle Verzeichnis nicht ignorieren, dann käme statt des eigentlich gewünschten Kommandos /usr/bin/mv das lokal lagernde, heimtückische Skript zur Ausführung. Sie würden mit der Dateioperation nicht die Daten auf die Festplatte schieben, sondern Ihr gesamtes Home-Verzeichnis unwiederbringlich ins Nirvana senden, ohne etwas davon zu bemerken – bis es zu spät wäre.
Listing 6
#!/bin/bash echo "mv mich und ich lösch dich!" rm -rf $HOME
Schlüpfriger Pfad
Die Shell stellt also sicher, dass stets dieselben, wohldefinierten Befehle zur Ausführung kommen, außer wenn Sie explizit per Dotslash etwas anderes anfordern. Können Sie sich mit diesem wohldurchdachten Verhalten trotzdem nicht anfreunden, steht es Ihnen jedoch frei, das aktuelle Verzeichnis in die $PATH-Variable aufzunehmen. Wohlgemerkt: Diese Aktion geschieht auf eigene Gefahr – wir empfehlen Ihnen, sich mit einem Dotslash anzufreunden.
Je nach Linux-Distribution und Shell passen Sie den $PATH über Einträge in die Dateien ~/.profile oder ~/.bashrc in Ihrem Home-Verzeichnis individuell an. Fügen Sie dazu am Ende der Datei das Kommando export PATH="$PATH:. ein (Listing 7, erste Zeile) und starten dann ein neues Terminalfenster, dann ähnelt der neue $PATH der Ausgabe aus der letzten Zeile von Listing 7.
Am Ende des Pfads steht nun der Punkt für das aktuelle Verzeichnis, sodass Sie den Dotslash weglassen dürfen (Listing 8). Weil der Verweis auf das aktuelle Verzeichnis am Ende des neuen Pfads steht, besteht auch keine Gefahr, dass der im letzten Abschnitt vorgestellte “Trick” funktioniert.
Listing 7
$ echo 'export PATH="$PATH:.' >> ~/.profile [... neues Terminal ...] $ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:.
Listing 8
tux@computer:~$ cd tmp tux@computer:~/tmp$ beispiel.sh Dies ist nur ein Beispiel für ein Skript.
<c>~/bin<c> und <c>/usr/local/bin<c>
Doch es gibt auch einen sozusagen kanonischen Weg, eigene Skripte ohne Pfadangaben auszuführen. Für an der Paketverwaltung vorbei installierte Anwendungen eröffnen die Ordner ~/bin/ und /usr/local/bin/ sinnvolle Alternativen dazu, den Pfad anzupassen. Zumindest einen dieser Ordner binden die meisten Distributionen automatisch in $PATH ein, sodass Sie hier Skripte oder Programme ablegen und von überall ohne eine Pfadangabe ausführen können. Verlinken Sie auch nach /opt/ installierte Anwendungen per Symlink hierher.
Fehlt der Ordner ~/bin/ in Ihrem Home-Verzeichnis noch, dann legen Sie ihn einfach an. Dann müssen Sie in der Regel auch noch einen passenden Eintrag in der Shell-Profildatei ~/.profile ergänzen (Listing 9), die das System beim Starten einer Shell ausführt. In anschließend neu geöffneten Terminalfenstern steht der Ordner $HOME/bin/ nun an erster Stelle des Pfads. Skripte, die Sie dort ablegen, werden automatisch gefunden.
Listing 9
# falls ein privates bin-Verzeichnis # existiert, dieses in $PATH einbinden if [ -d "$HOME/bin" ] ; then PATH="$HOME/bin:$PATH" fi
Sollen alle Benutzer des Systems Ihr Skript oder kleines Programm ausführen dürfen, dann sollten Sie es mit Root-Rechten nach /usr/local/bin/ kopieren oder es mit einem Symlink (ln -s QuelleZiel) dort verlinken. Dieses Verzeichnis befindet sich immer im $PATH, und die Paketverwaltung legt dort nie Daten oder Dateien ab, sodass Sie dieser mit Ihrem Skript nicht in die Quere kommen. Das Verzeichnis bleibt Ihr kleines Reich für Skripte und Programme.





