Linux und Ich

Blog über Ubuntu, Linux, Android und IT

dotslash

Über die Verwendung von ./ in Shell-Befehlen

| 26 Kommentare

Die Zeichenkombination ./ – also ein Punkt gefolgt von einem Schrägstich (auch bekannt als Slash) – wird oft vor Linux-Befehle gestellt. Besonders Linux-Einsteiger stolpern über diese Kombination, wenn es darum geht ein Programm oder ein Skript auszuführen, das im aktuellen Verzeichnis einer Shell liegt. Was steckt hinter diesem kryptischen Kommando und muss man es immer eingeben? Ich versuche den Dschungel etwas zu lüften.

Bevor ich zum ./ komme muss ich etwas ausholen und ein paar Grundlagen in Bezug auf die Linux-Shell und die Umgebungsvariable $PATH verlieren. Eine Shell ist ein Programm, das eine textbasierte Benutzerschnittstelle zur Verfügung stellt, in die der Benutzer Befehle eingeben kann, die dann letztendlich von der Shell ausgeführt werden.

Bei der Ausführung eines Befehls in Linux-Systemen wird immer entweder ein in der Shell fest eingebautes Kommando ausgeführt oder ein ausführbares Programm oder Skript, das sich irgendwo auf der Festplatte befindet. Zur ersten Gruppe gehören Befehle wie cd, ls, echo, kill oder alias. Diese Befehle sind fest in der Shell eingebaut, es gibt also nirgends ein Programm, das beim Aufruf dieser Befehle ausgeführt wird. Im Gegensatz dazu stehen ausführbare Programme wie mv, less, gedit oder gar firefox deren ausführbare Programmdateien oft im Verzeichnis /usr/bin stecken.

Gibt man nun einen Befehl in eine Shell ein, so überprüft die Shell erst einmal ob dieser Befehl eingebaut ist. Erkennt die Shell einen externen Befehl, so durchsucht die Shell den Pfad (über die Variable $PATH) nach einer ausführbaren Datei mit dem selben Namen und führt die Datei 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 Relevanz. Wird nach “foo” gesucht und in /usr/local/bin das Skript gefunden, dann wird /usr/local/bin/foo ausgeführt, auch wenn es in /usr/bin auch noch eine Datei namens “foo” gäbe. Diesen Punkt darf man nicht aus dem Gedächtnis verlieren.

Skripte in einem Verzeichnis ausführen

Nun kann ich den Bogen zum ./ langsam wieder schließen. Ihr wollt ein Skript oder ein Programm ausführen. Als Beispiel wähle ich ein Skript namens beispiel.sh im Verzeichnis ~/tmp in eurem Homeverzeichnis. Dazu öffnet ihr ein Terminal, gehe in das Verzeichnis in das sich das Skript befindet und versuche 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 und das obwohl ich mich im passenden Verzeichnis befinde. Das Skript da ist und die Rechte auch 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 das Kommando “beispiel.sh” kein eingebautes Shell-Kommando ist, daher wird im zweiten Schritt der Pfad durchsucht. Auch hier kommt die Shell wieder zu keinem positiven Ereignis. In allen Verzeichnissen, die im $PATH enthalten sind, wird keine Datei beispiel.sh gefunden, daher wird “command not found” ausgegeben. Was ist da 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 kompletten 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 zahllose Varianten den Pfad zur Datei anzugeben. Die Tipperei dabei ist recht aufwändig, daher gibt es Abkürzungen, die einem die Arbeit etwas abnehmen. Die kürzeste Variante einen Pfad zum aktuellen Verzeichnis anzugeben ist dabei der Punkt. Der Punkt steht für das aktuelle Verzeichnis und daher reicht zum Ausführen der beispiel.sh eben der ./ als Pfadangabe aus…

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 eventuell die Frage warum das aktuelle Verzeichnis nicht im Pfad steckt. Man würde sich doch immer etwas Tipparbeit und vor allen Dingen Verwirrung ersparen. Der Grund ist simpel: Sicherheit! Stellt euch vor jemand gibt euch ein paar Daten, die ihr via Shell irgendwo hin verschieben sollt. In den Daten steckt jedoch noch eine kleine unscheinbare Datei namens “mv” mit dem Inhalt…

#!/bin/bash
rm -rf $HOME

Geht ihr nun in das Verzeichnis und setzt den Befehl…

otto@computer:~$ cd ~/daten
otto@computer:~/daten$ mv beispiel.* /wohin/auch/immer

…ab, dann würde – solange das aktuelle Verzeichnis vor /usr/bin im $PATH stehen würde – das im Verzeichnis enthaltene mv-Skript ausgeführt werden und nicht /usr/bin/mv. Das Ergebnis? Euer gesamtes Homeverzeichnis würde kommentarlos und in einem Rutsch gelöscht werden.

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.

Kann ich das aktuelle Verzeichnis wirklich nicht in den $PATH bekommen?

Sollte der Ausschluss des aktuellen Verzeichnisses euch wirklich sehr stören, so ist es recht problemlos möglich das aktuelle Verzeichnis in den $PATH zu packen. Ob es sinnvoll ist, das mag ich aufgrund des oben genannten Negativ-Beispieles gar nicht weiter erörtern.

Den Pfad könnt Ihr über einen Eintrag in die Datei ~/.profile in eurem Homerzeichnis individuell anpassen. Fügt Ihr am Ende der Datei beispielsweise den Eintrag…

PATH="$PATH:."

… ein und startet eine neue Shell, dann sieht euer $PATH letztendlich 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. Via…

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 Trick von oben funktioniert, doch es gibt einen korrekten Weg eigene Skripte ohne Pfadangaben auszuführen.

Die Verzeichnisse ~/bin und /usr/local/bin

Ihr könnt in eurem Homeverzeichnis einen Ordner namens ~/bin anlegen. Beim Starten einer Shell wird automatisch überprüft, ob dieser Ordner existiert. Wird er gefunden, dann wird er automatisch in den $PATH aufgenommen. Dies funktioniert über den 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. Schaut ihr euch den $PATH nun an, so seht ihr…

$ 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. Soll euer Skript oder kleines Programm von allen Benutzern des Systems verwendet werden, dann solltest ihr es nach /usr/local/bin kopieren oder dort verlinken. Dieses Verzeichnis befindet sich auch immer im $PATH und die Paketverwaltung legt dort nie Daten oder Dateien ab. Es ist also euer kleines Reich.

Autor: Christoph Langner

Hallo, ich bin Christoph -- Linux-User, Blogger und pragmatischer Fan freier Software. Wie Ihr ohne Zweifel bemerkt haben solltet schreibe ich hier über Linux im Allgemeinen, Ubuntu im Speziellen, sowie Android und andere Internet-Themen. Wenn du Freude an meinen Artikel gefunden haben solltest, dann kannst du mir über Facebook, Google+ oder Twitter oder natürlich dem Blog folgen.

26 Kommentare

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

  2. Ich vermute dass nicht jede Distribution automatisch ~/bin in den Pfad aufnimmt. Hab jetzt aber auch keine Übersicht welche das macht und welche nicht.

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


  4. Julian:

    ls, echo und kill sind in keine Shell integriert sondern liegen in /bin.

    Jein, bei ls habe ich mich in der Tat geirrt, aber echo und kill sind in der Bash-Shell fest eingebaut. Siehe `man bash`.

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

  6. Eine wirklich gute und sehr verständlich geschriebne Erklärung,
    Vielen dank dafür

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

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

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

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

  11. Prima Zusammenfassung, vielen Dank!

  12. Pingback: Destillat #7 | duetsch.info - Open Source, Wet-, Web-, Software

  13. hey ho.
    danke für diesen interessanten artikel!
    hat mir endlich klarheit verschafft!

    Danke!

  14. @ Kommentar 13 von Gast: Das leuchtet mir ein – wie bekomme ich denn mein ~/bin ans Ende der $PATH-Variable?

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

  16. Sehr gut, Danke! Jetzt rag ich mich nur noch was sbin ist?

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

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

  19. um bei einer laufenden Bash die Veränderungen neu einzulesen reicht es eigentlich, die entsprechenden Dateien zu “sourcen”. Unter Bash klappt das mittels “. “

  20. Pingback: Aero Snap mit GNOME und Compiz (the next generation) | Linux und Ich

  21. Pingback: Streams aus der ZDF-Mediathek mit zdf-dl auf die Platte runterladen | Linux und Ich

Hinterlasse eine Antwort

Auf Linux und Ich darf anonym kommentiert werden. Die Felder für Name und E-Mail-Adresse dürfen beim Eintragen eures Kommentars leer bleiben. Ich freue mich aber über jeden Kommentar, zu dem der Autor mit seinem Namen steht.