Warum funktioniert os.path.join () in diesem Fall nicht?

324

Der folgende Code wird nicht verknüpft. Beim Debuggen speichert der Befehl nicht den gesamten Pfad, sondern nur den letzten Eintrag.

os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/')

Wenn ich dies teste, wird nur der /new_sandbox/Teil des Codes gespeichert.

chrissygormley
quelle

Antworten:

425

Die letzteren Zeichenfolgen sollten nicht mit einem Schrägstrich beginnen. Wenn sie mit einem Schrägstrich beginnen, werden sie als "absoluter Pfad" betrachtet und alles, was vor ihnen liegt, wird verworfen.

Zitieren der Python-Dokumente füros.path.join :

Wenn eine Komponente ein absoluter Pfad ist, werden alle vorherigen Komponenten weggeworfen und die Verknüpfung von der absoluten Pfadkomponente fortgesetzt.

Hinweis unter Windows: Das Verhalten in Bezug auf Laufwerksbuchstaben, das sich im Vergleich zu früheren Python-Versionen geändert zu haben scheint:

Unter Windows wird der Laufwerksbuchstabe nicht zurückgesetzt, wenn eine absolute Pfadkomponente (z. B. r'\foo') gefunden wird. Wenn eine Komponente einen Laufwerksbuchstaben enthält, werden alle vorherigen Komponenten weggeworfen und der Laufwerksbuchstabe wird zurückgesetzt. Beachten Sie, dass, da für jedes Laufwerk ein aktuelles Verzeichnis vorhanden ist, dies os.path.join("c:", "foo")einen Pfad relativ zum aktuellen Verzeichnis auf Laufwerk C:( c:foo) darstellt, nicht c:\foo.

Craig McQueen
quelle
85
-1: Keine Zeichenfolge sollte ein "/" enthalten. Ein ganzer Punkt von os.path.join ist es, zu verhindern, dass Schrägstriche in den Pfad eingefügt werden.
S.Lott
6
Das Problem mit str.join () ist natürlich, dass doppelte Schrägstriche nicht beseitigt werden. Ich denke, dies ist der Hauptzweck für Leute, die os.path.join verwenden. zB '/'.join(['/etc/', '/ conf']) führt zu drei Schrägstrichen: '/ etc /// conf'
Dustin Rasener
17
@DustinRasener Sie können verwenden os.path.normpath, um dieses Ziel zu erreichen.
Gareth Latty
5
Keine Ahnung, warum Menschen über das Verhalten von os.path.join frustriert sind. In anderen Sprachen verhält sich die entsprechende Pfadverknüpfungsbibliothek / -methode genauso. Es ist sicherer und sinnvoller.
Don Cheadle
19
Dies ist frustrierend, weil es implizite Magie ist , im Gegensatz zur Kardinalheuristik von "Explizit ist besser als implizit". Und das ist es auch . Sprachdesigner glauben vielleicht, dass sie es besser wissen, aber es gibt offensichtliche und nachweislich sichere Gründe, dies gelegentlich tun zu wollen. Jetzt können wir nicht. Deshalb können wir keine guten Dinge haben.
Cecil Curry
151

Die Idee von os.path.join()ist, Ihr Programm plattformübergreifend zu gestalten (Linux / Windows / etc).

Schon ein Schrägstrich ruiniert es.

Es ist also nur sinnvoll, wenn es mit einem Referenzpunkt wie os.environ['HOME']oder verwendet wird os.path.dirname(__file__).

Antony Hatchkins
quelle
75

os.path.join()kann in Verbindung mit verwendet werden os.path.sep, um einen absoluten und keinen relativen Pfad zu erstellen.

os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')
Ghammond
quelle
8
Die Verwendung os.path.sepals erstes Element zum Erstellen eines absoluten Pfades ist besser als jede andere Antwort hier! Der Sinn der Verwendung von os.pathstr-Methoden anstelle grundlegender str-Methoden besteht darin, das Schreiben zu vermeiden /. Es ist auch großartig, jedes Unterverzeichnis als neues Argument zu verwenden und alle Schrägstriche zu entfernen. Es wäre wahrscheinlich eine gute Idee, mit einem Scheck sicherzustellen, todaystrder nicht mit einem Schrägstrich beginnt! ;)
snooze92
3
Dies funktioniert auch unter Windows (Python 2.7.6). Es hat sich nicht in 'C: \' eingemischt und ist den Unterverzeichnissen beigetreten.
Rickfoosusa
23

Verwenden Sie keine Schrägstriche am Anfang von Pfadkomponenten, außer wenn Sie auf das Stammverzeichnis verweisen:

os.path.join('/home/build/test/sandboxes', todaystr, 'new_sandbox')

Siehe auch: http://docs.python.org/library/os.path.html#os.path.join

miku
quelle
21

Um zu verstehen, warum dieses überraschende Verhalten nicht ganz schrecklich ist, betrachten Sie eine Anwendung, die einen Konfigurationsdateinamen als Argument akzeptiert:

config_root = "/etc/myapp.conf/"
file_name = os.path.join(config_root, sys.argv[1])

Wenn die Anwendung ausgeführt wird mit:

$ myapp foo.conf

Die Konfigurationsdatei /etc/myapp.conf/foo.confwird verwendet.

Überlegen Sie jedoch, was passiert, wenn die Anwendung aufgerufen wird mit:

$ myapp /some/path/bar.conf

Dann myapp sollte die Konfigurationsdatei unter /some/path/bar.conf(und nicht /etc/myapp.conf/some/path/bar.confoder ähnlich) verwendet werden.

Es mag nicht großartig sein, aber ich glaube, dies ist die Motivation für das absolute Pfadverhalten.

David Wolever
quelle
Vielen Dank! Ich hatte dieses Verhalten immer gehasst, bis ich Ihre Antwort gelesen hatte! Es ist in docs.python.org/3.5/library/os.path.html#os.path.join dokumentiert , aber nicht die Motivation dafür.
Eli_B
In diesem Moment, in dem Sie genau die Lösung benötigen, halten viele Menschen dies für schrecklich.
Ashrasmun
12

Dies liegt daran, dass Ihr '/new_sandbox/'mit einem beginnt /und daher relativ zum Stammverzeichnis angenommen wird. Entfernen Sie die Leitung /.

Bernstein
quelle
8

Um Ihre Funktion portabler zu machen, verwenden Sie sie als solche:

os.path.join(os.sep, 'home', 'build', 'test', 'sandboxes', todaystr, 'new_sandbox')

oder

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')
NuclearPeon
quelle
8

Versuchen Sie die Kombination von split("/")und *für Zeichenfolgen mit vorhandenen Verknüpfungen.

import os

home = '/home/build/test/sandboxes/'
todaystr = '042118'
new = '/new_sandbox/'

os.path.join(*home.split("/"), todaystr, *new.split("/"))


Wie es funktioniert...

split("/") verwandelt vorhandenen Pfad in Liste: ['', 'home', 'build', 'test', 'sandboxes', '']

* vor der Liste bricht jedes Listenelement seinen eigenen Parameter aus

openwonk
quelle
3

Versuchen Sie es new_sandboxnur mit

os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')
DU
quelle
2

Mach es so, ohne die zusätzlichen Schrägstriche

root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")
Ghostdog74
quelle
0

Beachten Sie, dass ein ähnliches Problem Sie beißen kann, wenn Sie os.path.join()eine Erweiterung einfügen, die bereits einen Punkt enthält. Dies geschieht automatisch, wenn Sie diese verwenden os.path.splitext(). In diesem Beispiel:

components = os.path.splitext(filename)
prefix = components[0]
extension = components[1]
return os.path.join("avatars", instance.username, prefix, extension)

Auch wenn Sie extensionmöglicherweise .jpgeinen Ordner mit dem Namen "foobar" anstelle einer Datei mit dem Namen "foobar.jpg" haben. Um dies zu verhindern, müssen Sie die Erweiterung separat anhängen:

return os.path.join("avatars", instance.username, prefix) + extension
Shacker
quelle
0

Sie können stripdie '/':

>>> os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/'.strip('/'))
'/home/build/test/sandboxes/04122019/new_sandbox'
suhailvs
quelle
0

Ich würde empfehlen, die Zeichenfolge von der zweiten und den folgenden Zeichenfolgen zu entfernen os.path.sep, um zu verhindern, dass sie als absolute Pfade interpretiert werden:

first_path_str = '/home/build/test/sandboxes/'
original_other_path_to_append_ls = [todaystr, '/new_sandbox/']
other_path_to_append_ls = [
    i_path.strip(os.path.sep) for i_path in original_other_path_to_append_ls
]
output_path = os.path.join(first_path_str, *other_path_to_append_ls)
Igor Fobia
quelle
0
os.path.join("a", *"/b".split(os.sep))
'a/b'

eine umfassendere Version:

import os

def join (p, f, sep = os.sep):
    f = os.path.normpath(f)
    if p == "":
        return (f);
    else:
        p = os.path.normpath(p)
        return (os.path.join(p, *f.split(os.sep)))

def test (p, f, sep = os.sep):
    print("os.path.join({}, {}) => {}".format(p, f, os.path.join(p, f)))
    print("        join({}, {}) => {}".format(p, f, join(p, f, sep)))

if __name__ == "__main__":
    # /a/b/c for all
    test("\\a\\b", "\\c", "\\") # optionally pass in the sep you are using locally
    test("/a/b", "/c", "/")
    test("/a/b", "c")
    test("/a/b/", "c")
    test("", "/c")
    test("", "c")
Neil McGill
quelle
Was ist, wenn os.sep tatsächlich ist "\"? Dann wird Ihr erstes Beispiel os.path.join("a", *"/b".split("\\")), das ergibt "/b"... Ich bezweifle, dass dies das beabsichtigte Ergebnis ist.
NichtJens
1
Aktualisiert - Ich nehme an, Sie müssen einen Hinweis geben, wie der Pfad, den Sie lokal verwenden, unabhängig von dem des Betriebssystems ist, auf dem Sie ausgeführt werden
Neil McGill
1
Ja. Alternativ könnte man sich auf beide häufig verwendeten Optionen aufteilen ... aber dann könnte ein anderes Betriebssystem ein drittes entwickeln.
NichtJens