Die Zeichenkombination ./
– also ein Punkt gefolgt von einem Schrägstrich (auch bekannt als Slash) – wird häufig vor Linux-Befehlen verwendet. Besonders Linux-Einsteiger stoßen auf diese Kombination, wenn es darum geht, ein Programm oder ein Skript auszuführen, das im aktuellen Verzeichnis einer Shell liegt. Was verbirgt sich hinter diesem scheinbar kryptischen Kommando, und muss man es immer eingeben? Ich versuche, den Dschungel ein wenig zu lüften.
Bevor ich zum ./
komme, muss ich ein wenig ausholen und ein paar Grundlagen in Bezug auf die Linux-Shell und die Umgebungsvariable $PATH
erklären. Eine Shell ist ein Programm, das eine textbasierte Benutzerschnittstelle bereitstellt, in die der Benutzer Befehle eingeben kann, die dann von der Shell ausgeführt werden.
Bei der Ausführung eines Befehls in einem Linux-System wird entweder ein in der Shell fest eingebautes Kommando ausgeführt oder ein ausführbares Programm oder Skript, das sich irgendwo auf der Festplatte befindet. Zu den eingebauten Befehlen gehören beispielsweise cd
, echo
, kill
oder alias
. Diese Befehle sind fest in der Shell integriert, sodass beim Aufruf dieser Befehle kein externes Programm ausgeführt wird. Im Gegensatz dazu stehen ausführbare Programme wie mv
, less
, gedit
oder firefox
, deren ausführbare Dateien oft im Verzeichnis /usr/bin
liegen.
Gibt man nun einen Befehl in eine Shell ein, prüft die Shell zunächst, ob dieser Befehl eingebaut ist. Erkennt die Shell einen externen Befehl, durchsucht sie den Pfad (über die Variable $PATH
) nach einer ausführbaren Datei mit demselben Namen und führt diese aus, wenn die Suche erfolgreich war. Über…
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
…könnt ihr euch den aktuellen Suchpfad anzeigen lassen. In jedem dieser Verzeichnisse sucht die Shell nach ausführbaren Dateien. Die Reihenfolge der Einträge ist dabei von Bedeutung. Wird nach foo
gesucht und im Verzeichnis /usr/local/bin
das Skript gefunden, dann wird /usr/local/bin/foo
ausgeführt, auch wenn es in /usr/bin
noch eine Datei namens foo
gibt. Diesen Punkt sollte man nicht aus dem Gedächtnis verlieren.
Skripte in einem Verzeichnis ausführen
Nun kann ich den Bogen zum ./
wieder schließen. Angenommen, ihr wollt ein Skript oder ein Programm ausführen. Als Beispiel nehme ich ein Skript namens beispiel.sh
im Verzeichnis ~/tmp
in eurem Home-Verzeichnis. Öffnet dazu ein Terminal, geht in das Verzeichnis, in dem sich das Skript befindet, und versucht, es aufzurufen…
otto@computer:~$ cd tmp
otto@computer:~/tmp$ beispiel.sh
beispiel.sh: command not found
otto@computer:~/tmp$ ls -al beispiel.sh
-rwxr-xr-x 1 otto otto 61 2010-02-03 15:28 beispiel.sh
Wie ihr seht, wird das Skript nicht gefunden, obwohl ich mich im richtigen Verzeichnis befinde, das Skript da ist und die Berechtigungen stimmen. Warum klappt das also nicht? Die Lösung liegt im vorhin beschriebenen $PATH
. Zur Erinnerung gebe ich ihn nochmal aus…
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
Die Shell erkennt schnell, dass beispiel.sh
kein eingebauter Shell-Befehl ist, daher durchsucht sie den Pfad. Auch hier wird sie jedoch nicht fündig. In den Verzeichnissen, die im $PATH
enthalten sind, wird keine Datei namens beispiel.sh
gefunden, weshalb die Shell „command not found“ ausgibt. Was ist also los? Der Grund ist einfach: Der $PATH
enthält nie das aktuelle Verzeichnis, in dem man sich befindet. Um also mein Skript beispiel.sh
auszuführen, müsste ich den vollständigen Pfad angeben…
otto@computer:~$ cd tmp
otto@computer:~/tmp$ /home/otto/tmp/beispiel.sh
Dies ist nur ein Beispiel für ein Skript.
otto@computer:~/tmp$ $HOME/tmp/beispiel.sh
Dies ist nur ein Beispiel für ein Skript.
otto@computer:~/tmp$ ~/tmp/beispiel.sh
Dies ist nur ein Beispiel für ein Skript.
Wie ihr seht, gibt es zahlreiche Varianten, den Pfad zur Datei anzugeben. Das Tippen dabei ist jedoch aufwändig, weshalb es Abkürzungen gibt, die einem die Arbeit erleichtern. Die kürzeste Möglichkeit, einen Pfad zum aktuellen Verzeichnis anzugeben, ist der Punkt. Der Punkt steht für das aktuelle Verzeichnis, sodass zum Ausführen von beispiel.sh
einfach ./
als Pfadangabe ausreicht…
otto@computer:~$ cd tmp
otto@computer:~/tmp$ ./beispiel.sh
Dies ist nur ein Beispiel für ein Skript.
Warum ist das aktuelle Verzeichnis nicht im Pfad?
Ihr stellt euch nun möglicherweise die Frage, warum das aktuelle Verzeichnis nicht im Pfad enthalten ist. Man würde sich doch einiges an Tipparbeit und vor allem Verwirrung ersparen. Der Grund ist einfach: Sicherheit! Stellt euch vor, jemand gibt euch ein paar Daten, die ihr via Shell irgendwohin verschieben sollt. In den Daten befindet sich jedoch eine unscheinbare Datei namens mv
mit folgendem Inhalt…
#!/bin/bash
rm -rf $HOME
Geht ihr nun in das Verzeichnis und gebt den Befehl…
otto@computer:~$ cd ~/daten
otto@computer:~/daten$ mv beispiel.* /wohin/auch/immer
…ein, dann würde – solange das aktuelle Verzeichnis vor /usr/bin
im $PATH
steht – das im Verzeichnis enthaltene mv
-Skript ausgeführt werden und nicht das /usr/bin/mv
. Das Ergebnis? Euer gesamtes Home-Verzeichnis würde kommentarlos und auf einen Schlag gelöscht werden. Wäre das aktuelle Verzeichnis im $PATH
vor den Systempfaden wie /usr/bin
und Co. eingetragen, könnte man ohne Weiteres schadhafter Software oder sogar Schadcode unterjubeln.
Kann ich das aktuelle Verzeichnis wirklich nicht in den $PATH
aufnehmen?
Falls euch der Ausschluss des aktuellen Verzeichnisses wirklich stört, ist es problemlos möglich, das aktuelle Verzeichnis in den $PATH
aufzunehmen. Ob es sinnvoll ist, darüber möchte ich aufgrund des oben genannten Negativbeispiels nicht weiter spekulieren. Den Pfad könnt ihr über einen Eintrag in der Datei ~/.profile
in eurem Home-Verzeichnis anpassen. Fügt ihr am Ende der Datei beispielsweise diesen Eintrag hinzu…
PATH="$PATH:."
…und startet eine neue Shell, sieht euer $PATH
schließlich so aus.
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:.
Am Ende steht der .
für das aktuelle Verzeichnis. Mit…
otto@computer:~$ cd tmp
otto@computer:~/tmp$ beispiel.sh
Dies ist nur ein Beispiel für ein Skript.
…könnt ihr dann das Beispiel-Skript ausführen. Ihr seht, kein ./
mehr nötig. Steht der Punkt am Ende des Pfades, so besteht auch keine Gefahr, dass der oben beschriebene Trick funktioniert, aber es gibt einen sicheren Weg, eigene Skripte ohne Pfadangaben auszuführen.
Die Verzeichnisse ~/bin
und /usr/local/bin
Ihr könnt in eurem Home-Verzeichnis einen Ordner namens ~/bin
anlegen. Beim Starten einer Shell wird automatisch geprüft, ob dieser Ordner existiert. Wird er gefunden, wird er automatisch in den $PATH
aufgenommen. Dies funktioniert über den folgenden Abschnitt…
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi
…in der Datei ~/.profile
, die beim Starten einer Shell ausgeführt wird. Wenn ihr euch den $PATH
nun anseht, wird er folgendermaßen aussehen…
$ echo $PATH
/home/otto/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
Der Ordner /home/otto/bin
steht nun an erster Stelle, und Skripte, die ihr dort ablegt, werden automatisch gefunden. Wenn ihr wollt, dass euer Skript oder kleines Programm von allen Benutzern des Systems verwendet wird, solltet ihr es nach /usr/local/bin
kopieren oder dort verlinken. Dieses Verzeichnis befindet sich ebenfalls immer im $PATH
, und die Paketverwaltung legt dort keine Dateien ab. Es ist also euer persönliches Verzeichnis.
Schöne Zusammenfassung für Einsteiger. 🙂 Werde ich nächstes mal jemandem direkt verlinken, wenn er nach ./ fragt.
Noch eine Frage dazu, da ich das Thema letzt erst hatte: Wie „standardkonform“ ist es, z.B. heruntergeladene Java-Programme oder sonstiges nach ~/bin anstatt nach /opt zu verfrachten? Will heißen: Ist ~/bin distributionsübergreifend?
Ich vermute dass nicht jede Distribution automatisch ~/bin in den Pfad aufnimmt. Hab jetzt aber auch keine Übersicht welche das macht und welche nicht.
schöner Artikel, Danke
ls, echo und kill sind in keine Shell integriert sondern liegen in /bin.
Noch eine kleine Ergänzung: ~/.profile stimmt glaub ich nur bei einer login-shell. Ansonsten ist es ~/.bashrc.
Nachzulesen in der manpage von bash.
Jein, bei ls habe ich mich in der Tat geirrt, aber echo und kill sind in der Bash-Shell fest eingebaut. Siehe `man bash`.
Du hättest vielleicht noch auf den Selektor .. eingehen sollen für den übergeordneten Pfad. Den man übrigens auch mehrfach kombinieren kann, um mit ../../../beispiel.sh zum Beispiel das Skript 3 Ebenen über dem aktuellen Ordner findet.
Eine wirklich gute und sehr verständlich geschriebne Erklärung,
Vielen dank dafür
@Julian – es gibt sie AUCH in /bin, für all jene Shells die es nicht implementieren (oder wenn die Funktionalität warum auch immer mal kaputt sein sollte). Die bash hat sie aber auch als builtins. Du kannst es selber probieren mit dem (bash-builtin kommando) „type“, e.g.
$ type echo
echo is a shell builtin
Wenn man sich die manpage zu echo ansieht wird dort auch darauf hingewissen, dass es sich um ein shell-builtin handeln kann: Den die haben durchaus (leider) manchmal ein unterschiedliches Verhalten…
Irgendwie scheint mir dein Post nicht ganz logisch…
„Würde das aktuelle Verzeichnis im Suchpfad vor den Systempfaden /usr/bin und Co. im $PATH stehen, dann könnte man ohne Probleme dem Benutzer maliziöse Skripte oder gar Programme unterschieben.“
Auf die Idee würden wohl wenige kommen, da wenige ihre Standardbefehle manipulieren wollen. Also würde man das aktuelle Verzeichnis eh (wie von dir beschrieben) ans Ende tun. Die Sicherheit ist damit dann auch gegeben, da man nur mit Root-Rechten an /usr/bin etc dran kommt und Befehle wie mv richtig gefunden werden.
Gleichzeitig ist es für die Sicherheit sicherlich nicht zuträglich die Verzeichnisse ~/bin und /usr/local/bin ans Anfang der Liste zu stellen – erstens gibt’s dafür keinen vernünftigen Grund (siehe oben) und zweitens ließen sich dann auch ohne Root-Rechte leicht eigene Skripte einschleusen, die eventuell Standardbefehle überschreiben…
Sehr gute Zusammenfassung!
Deine guten Ausführungen treffen häufig nicht auf „Shells“ allgemein zu, so durchsuchen einige durchaus das wd und parallel noch andere Pfade wie meinetwegen alles relativ zum Heimatverzeichnis. Das gilt natürlich insbesondere für Builtins (kill usw.) hier geht Bash gerne seine eigenen verworrenen Wege (abseits vom „UNIX-Weg“). Auch ist PATH ansonsten gerne mal ein „richtiges“ Array.
Na dieser Artikel ist mal wirklich sehr, sehr auführlich 🙂
Ich schließe mich zerwas an, den Artikel werde ich bei Fragen zur Ausführung von Scripten auch verlinken 😉
Prima Zusammenfassung, vielen Dank!
hey ho.
danke für diesen interessanten artikel!
hat mir endlich klarheit verschafft!
Danke!
@ Kommentar 13 von Gast: Das leuchtet mir ein – wie bekomme ich denn mein ~/bin ans Ende der $PATH-Variable?
Was ich auch mal kurz sagen wollte: unter Windows gibt es _auch_ eine PATH-Variable; kann man sich unter Systemsteuerung–>System–>Erweitert–>Umgebungsvariablen (oder so, hab gerade kein Windows zur Hand) anschauen. Gilt zumindest bis einschließlich XP.
Der einzige Unterschied zu Linux: unter Windows (bzw der Eingabeaufforderung) wird _zuerst_im aktuellen Pfad geschaut, und _danach_ die Verzeichnisse in der Variable durchsucht.
Außerdem (nur so am Rande) kann man einstellen, in welcher Reihenfolge nach übereinstimungen mit ausführbaren Dateiendungen gesucht wird.
Das haben früher sehr viele Viren ausgenutzt.
Das mit der Pfadvariable findet man spätestens dann heraus, wenn man selber anfäng zu Programmieren (ARM-Tools, Java, usw)
Sehr gut, Danke! Jetzt rag ich mich nur noch was sbin ist?
Hallo Marcus, meinst du /sbin oder /usr/sbin? In /sbin stecken für das System extrem wichtige binäre Programme, deswegen nennt es sich „system binaries. In /usr/sbin liegen Programme, die nicht ganz so wichtig sind. Hintergrund dessen ist die Möglichkeit das Dateisystem über mehrere Festplatten oder gar Systeme zu verteilen. Sollte /usr ausfallen, dann kann das System wenigstens noch ordentlich runterfahren. Siehe auch http://wiki.ubuntuusers.de/Verzeichnisstruktur#sbin
Hallo Chrisss,
schön zusammengefasst, aber eine Problematik bei mir.
Ich leere den Pfad a la $ [export] PATH=
damit ist $PATH leer. Stelle ich mich jetzt nach /bin oder in ein anderes [s]bin-Verzeichnis, kann ich einfach nur den jeweiligen befehl eingeben und es funktionert.
ich habe erwartet in /bin stehend : ./ls zu machen
aber es funktioniert auch nur ls. Warum ?
Die Umgebungsvariable wird erst wieder beim erneuten Einloggen ausgelesen. Du müsstest also in der ~/.profile den PATH leeren und dann ein weiteres Terminal öffnen. In diesem sollte das dann wie von dir beschrieben „klappen“.
Hi, hab mich vielleicht falsch ausgedrückt.
Innerhalb der Shell hab ich die Variable mit „PATH=“ geleert. Das führt dazu, dass z.B. unter / eine Eingabe von „ls“ mit „ls: command not found“ angegeben wird. Unter /bin
funktioniert ls aber trotzdem ohne PATH. Also „echo $PATH“ bringt nichts als gähnende Leere.
Ich hab aus Sicherheit sogar für den User innerhalb der /etc/profile den PATH geleert und alle login-Scripte im Heimatverzeichnis vorsorglich vom PATH Eintrag gesäubert. Problem bleibt.
Testsysteme: Ubuntu 9.04 und Debian 5.0.3 Lenny
um bei einer laufenden Bash die Veränderungen neu einzulesen reicht es eigentlich, die entsprechenden Dateien zu „sourcen“. Unter Bash klappt das mittels „. „
Hallo Christoph.
Ich bin echt begeistert!
Endlich mal verständlich erklärt. Eigentlich ganz einfach 🙂
Danke
Wie bekomme ich ein „Testscript.bash“ in /home/meinName/bin/?
wie bekommen ich eine „Testscript.bash“-Datei in /home/user/bin/?
Weis jetzt nicht wie du das genau meinst:
entweder erstellen: cd /home/user/bin/; nano Testscript.bash
oder verschieben: mv Testscript.bash /home/user/bin/
oder kopieren: cp Testscript.bash /home/user/bin/
oder verlinken: cd /home/user/bin/; ln -s /Pfad/zum/Testscript.bash Testscript.bash
Wenn der Pfad außerhalb deines Homeverzeichnisses liegt, dann musst du noch sudo vor die Befehle setzten um die Schreibrechte zu bekommen.