Relative Pfade

In einigen Delphi-Foren ist immer wieder zu hören „Niemals relative Pfade!“ Und auch ich behaupte das recht häufig. Und das nicht ganz ohne Grund. Relative Pfade sind die Ursache von vielen Problemen. Beispielsweise auch die Ursache des momentan diskutierten „Binary Planting“-Problems (siehe hierzu auch: Wikipedia). Also: niemals relative Pfade! Das Problem ist: Diese Behauptung ist falsch. Zumindest in der Absolutheit, wie die der Satz suggeriert.

Die Antwort auf so gut wie jede Frage im Software Engineering heißt ja bekanntlich „it depends“. Und so ist es auch mit der Antwort auf die Frage, ob relative Pfade „böse“ sind. Kommt drauf an. Das „niemals“ in obigem Satz ist also – wie fast immer – ein „niemals außer es gilt was anderes“… 😉

Das, was ich im Folgenden beschreibe, sind nur die grundlegenden Konzepte. Details sind vereinfacht. Ich will hier nur die Idee vermitteln, kein Handbuch schreiben.

Aber mal der Reihe nach…

Absoluten und Relative Pfade

Dateisysteme sind – zumindest näherungsweise – immer Bäume. [1] Ein absoluter Pfad ist nun ein solcher, der an der Wurzel beginnt. Beispiel:

Hier ein Teil eines typischen Linux-Systems:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/
L bin
L dev
L etc
L home
|   L alice
|   |   L Desktop
|   |   L Documents
|   |       L myfile
|   L bob
|   |   L Desktop
|   |   L Documents
|   |       L myfile
L lib
L usr
|   L bin
|   L local
|   |   L bin
|   |   L lib
|   |   L share
|   L share
L var

Ein absoluter Pfad wäre beispielsweise /home/alice/Desktop/myfile. Der Pfad beginnt direkt bei der Wurzel. Auch /home/alice wäre ein absoluter Pfad. Dieser verweist eben auf ein Verzeichnis anstatt auf eine reguläre Datei. Trotzdem ist es ein absoluter Pfad, weil er bei der Wurzel beginnt.

Bei einem Windows-System gibt es kein allgemeines Wurzelverzeichnis. Stattdessen gibt es pro Laufwerk eine Wurzel: C:\, D:\, E:\, etc. sind die Wurzeln der einzelnen Laufwerke, die je einen Baum darstellen.

C:\Users\alice\Desktop\myfile wäre somit ein absoluter Pfad unter Windows.

Relative Pfade beginnen nicht an der Wurzel, sondern Bilden nur einen Teil des vollständigen Pfades ab: Desktop/myfile ist somit ein relativer Pfad. Relative Pfade müssen immer vom System zu einem absoluten Pfad ergänzt werden. Dies kann der Programmierer selbst tun oder dem System überlassen. Letztendlich verarbeitet das Dateisystem aber nur absolute Pfade.

Ein wichtiger Punkt hierbei ist nun, dass relative Pfade immer relativ zu einem anderen – absoluten – Pfad sind. Desktop/myfile relativ zu /home/alice verweist auf /home/alice/Desktop/myfile während es relativ zu /home/bob auf /home/bob/Desktop/myfile verweist.

Man kann auch in andere Teilbäume verweisen. Dazu benutzt man ‚..‚ auf auf das übergeordnete Verzeichnis zu verweisen. ../bob/Desktop/myfile relativ zu /home/alice verweist beispielsweise auf /home/bob/Desktop/myfile.

Das macht relative Pfade sehr flexibel. Ein Teilbaum mit relativen Verweisen kann beliebig verschoben werden ohne, dass man die Verweise ändern müsste. Bei HTML-Seiten wird dies beispielsweise häufig so gemacht:

1
<img src="img/mypicture.jpg" alt="" />

Hier wird eine Grafik über einen relativen Pfad eingebunden. So funktioniert die HTML-Seite sowohl lokal auf dem eigenen Rechner, als auch später auf dem Server. Auch bei Änderung der Domain, bei Verschieben in ein anderes Verzeichnis, o.ä. bleibt der Pfad gültig. Mit absoluten Pfaden wäre das nicht möglich.

Ein weiteres Beispiel sind Konsolenprogramme. Diese arbeiten grundsätzlich relativ zu einem Arbeitsverzeichnis (auch: „aktuelles Verzeichnis“).

1
user@host:/home/alice/sources/foobar$ gcc foo.c -o foo

Der gcc liest die Datei foo.c (relativer Pfad) aus dem Arbeitsverzeichnis /home/alice/sources/foobar (absoluter Pfad) und speichert das Kompilat unter foo wieder im Arbeitsverzeichnis. Gäbe es keine relativen Pfade und kein Arbeitsverzeichnis, müsste man jeweils den kompletten absoluten Pfad angeben, was die Benutzung recht unangenehm machen würde.

Auch bei GUI-Anwendungen existiert das Arbeitsverzeichnis. Nur wird es hier seltener genutzt.

Das Arbeitsverzeichnis ist aber nicht das einzige Verzeichnis, zu dem ein Pfad relativ sein kann. So gibt es beispielsweise eine Umgebungsvariable $PATH, die Verzeichnisse auflistet, in denen sich ausführbare Dateien – Programme – befinden. In obigem Beispiel befindet sich gcc beispielsweise nicht im Arbeitsverzeichnis, sondern unter /usr/bin/gcc. /usr/bin befindet sich in der $PATH-Variable und somit wird (u.a.) dort nach dem gcc gesucht und dieser auch gefunden. gcc ist also relativ zu einem der Pfade, die in $PATH angegeben sind. Genauer müsste man sagen: relativ zu allen diesen, denn es werden alle diese durchprobiert, bis ein gcc gefunden ist.

Solche Listen mit relativen Pfaden gibt es mehrere. Manche sind implizit und nicht änderbar, andere sind explizit (wie die $PATH-Variable). So gibt es auch eine solche für Pfade, wo dynamisch gelinkte Bibliotheken gesucht werden. Unter Windows ist das Arbeitsverzeichnis Teil dieser (impliziten) Liste und das ist das Kern des Problems Binary Planting.

Installationsarten

Dabei gibt es grundlegend zwei Arten von Anwendungen bzw. besser: zwei Möglichkeiten, wie man Anwendungen installieren kann. Entweder „normal“, also dort, wo alle anderen Programme installiert sind oder per xcopy-Install, also durch einfaches Entpacken eines Archivs oder Kopieren eines Verzeichnisses. Letztere Möglichkeit ist quasi eine „portable“ Version, die man auch beispielsweise auf einem USB-Stick installieren kann.

Dabei sollte ein xcopy-Install am eigentlichen System möglichst nichts ändern, sondern ausschließlich Daten (Konfigurationsdateien, etc.) im gewählten Verzeichnis ablegen und keine oder zumindest wenige Abhängigkeiten nach außen haben.

Eine richtige Installation verhält sich hier ganz anders. Diese speichert Konfigurationsdateien im home-Verzeichnis des Benutzers und hat Abhängigkeiten zu Installierten Libraries. So hat jeder Benutzer seine eigene Konfiguration und es werden keine Libraries doppelt installiert, wie das u.U. bei einem xcopy-Install der Fall wäre.

Im Idealfall bietet man dem User beide Möglichkeiten der Installation an.

Es gibt nun mehrere Möglichkeiten wie man mit relativen und absoluten Pfaden umgeht. Insbesondere handhaben es Windows und Unix/Linux unterschiedlich.

Der Windows-Weg

Unter Windows ist immer klar, wo sich ein Programm befindet. Das Programm erhält diese Information direkt in der Parameterliste. Das Array mit den Parametern speichert unter dem Index 0 den absoluten Pfad des Programms. Bei xcopy-Install kann ein Programm also beispielsweise folgendermaßen eine Konfigurationsdatei schreiben:

1
2
3
4
public static method main(AArgs: array of string): Void;
begin
  writeConfigFile(ExtractFilePath(AArgs[0]) +"config.cfg");
end;

Aus dem Pfad der ausführbaren Datei (beispielsweise C:\sonstwo\foobar.exe) wird der Dateiname abgeschnitten, sodass man den Pfad des Installationsverzeichnisses (C:\sonstwo\) erhält. Dann wird
GeSHi Error: GeSHi could not find the language pseudocode (using path /homepages/28/d20958738/htdocs/christian-rehn.de/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
ergänzt. Im Prinzip macht hierbei der Programmierer aus dem relativen Pfad config.cfg einen absoluten.

Das könnte man theoretisch auch dem System überlassen:

1
2
3
4
public static method main(AArgs: array of string): Void;
begin
  writeConfigFile("config.cfg");
end;

Hierbei wird ein relativer Pfad übergeben, der vom System ergänzt wird. Wie oben erwähnt, haben auch GUI-Anwendungen ein Arbeitsverzeichnis. Meist ist das Installationsverzeichnis. Deshalb funktioniert das meistens. Das Arbeitsverzeichnis kann aber auch ein anderes sein. Es kann beispielsweise bei Starten über eine Verknüpfung anders gesetzt sein. Aber auch ein einfacher „Datei öffnen“-Dialog ändert das Arbeitsverzeichnis. Somit würde man die Konfigurationsdatei irgendwo auf die Platte schreiben, nur nicht da, wo sie hingehört. Das ist der Grund, warum man oftmals hört „niemals relative Pfade!“.

Bei einer richtigen Installation sollte man die Konfigurationsdateien im AppData-Verzeichnis des Benutzers (C:\Users\<Benutzer>\AppData\Roaming o.ä.; der genaue Pfad hängt von der Windows-Version ab) speichern. Dieses Verzeichnis kann man über die Prozedur SHGetFolderPath() erfragen.

Unter Unix

Unter Unix sieht das anders aus. Hier ist erstmal nicht so einfach klar, wo die ausführbare Datei nun letztendlich liegt. Im ersten Parameter (Index 0) kann auch ein Softlink oder ein relativer Pfad stehen. Das ist kein Fehler sondern prinzipbedingte Folge der Art, wie Unix Dateisysteme sieht. Unter Unix gilt der Grundsatz „everything is a file“. TCP-Verbindungen sind Dateien, Geräte sind Dateien, … alles ist eine Datei. Und Dateien werden nach einem bestimmten Prinzip in die Dateisystemhierarchie einsortiert. So stehen globale Konfigurationsdateien beispielsweise immer unter /etc und Bibliotheken unter /lib, /usr/lib, /usr/local/lib u.a. Die Unterschiede der einzelnen lib-Verzeichnisse sind hier nicht weiter relevant. Jedenfalls gibt es mehrere Stellen, an denen so eine Bibliothek sein kann, aber es ist normalerweise nicht der selbe Ort an dem auch die Executable liegt.

Bei normaler Installation wird normalerweise der absolute Pfad zu einer Library oder globalen Konfigurationsdatei in das Executable geschrieben [2][3]. Benutzerspezifische Konfigurationsdateien landen im Home-Verzeichnis des Benutzers, das mit ~ bezeichnet ist. ~ verweist also auf /home/alice.

Auch ein xcopy-Install ist unter Unix möglich. Hierbei werden Wrapper-Skripte eingesetzt, die Symlinks und relative Pfade auflösen und dann das eigentliche Programm starten. So wird dann sicher gestellt, dass irgendwo auf der Platte herumgeschrieben wird.

[1] Näherungsweise, weil es noch Hard- und Softlinks geben kann, die das aufweichen.
[2] Es gibt hier Ausnahmen, auf die ich hier aber nicht näher eingehen will.
[3] Das Tool ldd gibt zu einer Executable die Pfade zu den zu linkenden Bibliotheken aus.

5 Kommentare



  1. Hallo. Mein Projekt, ertstelle eine offline-html-Datei, welche dem Windows 10 KachelnMenu/ StartMenu -nachempfunden wurde. Darin sind/ Sollen werden)_
    – Windows Verzeichnisse der eigenen Dateien/ Fotos, Bilder, Videos, zuletzt geöffnet, Kontakte…
    – Programme, unter Windows vorinstalliert
    – Downloadlinks zum StandardEquipment an gängiger Freeware

    das ganze mit nettem Hintergrundbild (wie Win 10 Startmenu im Volbildmodus).
    —> Also eine Bibliothek, die man im Webbrowser starten können soll. Soweit.

    Frage, wie kann ich von den verschiedenen eigenen Dateien die Pfade so angeben, dass es keinen Unterschied macht, wohin man die Ordner (zB. Musik) verschoben hat oder welcher Nutzer angemeldet ist? Das soll auch auf anderen (Windows) Rechnern funktionieren.


  2. Es gibt WikAPI-Funktionen, die die Pfade ermitteln können. Aus einer reinen HTML-Seite heraus wirst du die aber nicht aufrufen können. Wenn das ginge, wäre das ein Sicherheitsrisiko. ggf. kannst du Umgebungsvariablen in den Pfaden nutzen. Ob es für alle von dir gewünschten Verzeichnisse solche gibt und wie die heißen, weiß ich aber nicht. Ich hab schon seit Jahren kein Windows mehr angefasst.


  3. Hi, ab der „Windows Weg“ gibt es Fehlermeldungen, z.B.

    GeSHi Error: GeSHi could not find the language pseudocode (using path /homepages/28/d20958738/htdocs/christian-rehn.de/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

    Am besten fixen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert