Jak uzyskać katalog nadrzędny w Pythonie?

350

Czy ktoś mógłby mi powiedzieć, jak uzyskać katalog nadrzędny ścieżki w Pythonie na wiele platform. Na przykład

C:\Program Files ---> C:\

i

C:\ ---> C:\

Jeśli katalog nie ma katalogu nadrzędnego, zwraca sam katalog. Pytanie może wydawać się proste, ale nie mogłem go wykopać przez Google.

Mridang Agarwalla
źródło

Odpowiedzi:

481

Aktualizacja z Python 3.4

Użyj pathlibmodułu.

from pathlib import Path
path = Path("/here/your/path/file.txt")
print(path.parent)

Stara odpowiedź

Spróbuj tego:

import os.path
print os.path.abspath(os.path.join(yourpath, os.pardir))

gdzie yourpathjest ścieżka, dla której chcesz nadrzędny.

kender
źródło
137
Twoja odpowiedź jest poprawna, ale zawiła; os.path.dirnamejest funkcją tego, jak a+=5-4jest bardziej skomplikowany niż a+=1. Pytanie dotyczyło tylko katalogu nadrzędnego, a nie tego, czy istnieje lub czy prawdziwy katalog nadrzędny, zakładając, że przeszkadzają mu dowiązania symboliczne.
tzot
16
To os.pardirnie os.path.pardir.
bouteillebleu,
9
@bouteillebleu: Oba os.pardiri os.path.pardirsą w rzeczywistości poprawne (są identyczne).
Eric O Lebigot,
45
@tzot: niestety os.path.dirnamedaje różne wyniki w zależności od tego, czy na ścieżce znajduje się ukośnik końcowy. Jeśli chcesz uzyskać wiarygodne wyniki, musisz użyć os.path.joinmetody opisanej powyżej.
Artfunkel
21
Ponieważ jest to najwyraźniej wystarczająco skomplikowane, aby uzasadnić pytanie StackOverflow, uważam, że należy to dodać do biblioteki os.path jako funkcję wbudowaną.
antred
324

Używanie os.path.dirname:

>>> os.path.dirname(r'C:\Program Files')
'C:\\'
>>> os.path.dirname('C:\\')
'C:\\'
>>>

Zastrzeżenie: os.path.dirname()daje różne wyniki w zależności od tego, czy końcowy ukośnik jest zawarty na ścieżce. To może, ale nie musi być semantyka, którą chcesz. Por. Odpowiedź @ kender za pomocą os.path.join(yourpath, os.pardir).

Wai Yip Tung
źródło
6
os.path.dirname(r'C:\Program Files')co? Python po prostu podaje katalog, w którym powinien znajdować się plik „Program Files”. Co więcej, nawet nie musi istnieć, oto: os.path.dirname(r'c:\i\like\to\eat\pie')wyjścia'c:\\i\\like\\to\\eat'
Nick T
41
Oryginalny plakat nie stwierdza, że ​​katalog musi istnieć. Istnieje wiele metod nazw ścieżek, które wykonują jedynie operacje na łańcuchach. Aby sprawdzić, czy nazwa ścieżki faktycznie istnieje, wymaga dostępu do dysku. Zależy to od zastosowania, może to być pożądane lub nie.
Wai Yip Tung
10
to rozwiązanie jest wrażliwe na końcowe os.sep. Powiedz os.sep == '/'. dirname (foo / bar) -> foo, ale dirname (foo / bar /) -> foo / bar
marcin
6
To z założenia. Wszystko sprowadza się do interpretacji ścieżki ze znakiem końcowym /. Czy uważasz, że „ścieżka 1” jest równa „ścieżce 1 /”? Biblioteka używa najbardziej ogólnej interpretacji, że są odrębne. W pewnym kontekście ludzie mogą chcieć traktować je jako równoważne. W takim przypadku możesz najpierw wykonać pas startowy ('/'). Gdyby biblioteka wybrała inną interpretację, stracilibyście wierność.
Wai Yip Tung,
3
@ Ryan, nie wiem o tym. Istnieje cały dokument RFC 1808 napisany w celu rozwiązania problemu ścieżki względnej w URI i całej subtelności obecności i braku końcowego /. Jeśli znasz jakąkolwiek dokumentację, która mówi, że powinny one być traktowane ogólnie jako równoważne, proszę to zaznaczyć.
Wai Yip Tung
112

Metoda Pathlib (Python 3.4+)

from pathlib import Path
Path('C:\Program Files').parent
# Returns a Pathlib object

Tradycyjna metoda

import os.path
os.path.dirname('C:\Program Files')
# Returns a string


Jakiej metody powinienem użyć?

Użyj tradycyjnej metody, jeśli:

  • Martwisz się o istniejące błędy generowania kodu, jeśli miałby on używać obiektu Pathlib. (Ponieważ obiektów Pathlib nie można łączyć z łańcuchami.)

  • Twoja wersja Pythona jest mniejsza niż 3,4.

  • Potrzebujesz łańcucha i otrzymałeś ciąg. Załóżmy na przykład, że masz ciąg reprezentujący ścieżkę do pliku i chcesz uzyskać katalog nadrzędny, aby móc umieścić go w ciągu JSON. Byłoby głupotą konwersja na obiekt Pathlib i powrót w tym celu.

Jeśli żadne z powyższych nie ma zastosowania, użyj Pathlib.



Co to jest Pathlib?

Jeśli nie wiesz, co to jest Pathlib, moduł Pathlib to wspaniały moduł, który sprawia, że ​​praca z plikami jest jeszcze łatwiejsza. Większość, jeśli nie wszystkie wbudowane moduły Pythona, które działają z plikami, akceptują zarówno obiekty, jak i łańcuchy Pathlib. Podkreśliłem poniżej kilka przykładów z dokumentacji Pathlib, które pokazują niektóre fajne rzeczy, które możesz zrobić z Pathlib.

Poruszanie się po drzewie katalogów:

>>> p = Path('/etc')
>>> q = p / 'init.d' / 'reboot'
>>> q
PosixPath('/etc/init.d/reboot')
>>> q.resolve()
PosixPath('/etc/rc.d/init.d/halt')

Właściwości ścieżki zapytania:

>>> q.exists()
True
>>> q.is_dir()
False
wp-overwatch.com
źródło
4
To jedyna rozsądna odpowiedź. Jeśli jesteś zmuszony używać Pythona 2, po prostu pip install pathlib2i skorzystaj z backportu.
Navin
1
To rozwiązanie NIE jest wrażliwe na końcowe os.sep!
Dylan F
35
import os
p = os.path.abspath('..')

C:\Program Files ---> C:\\\

C:\ ---> C:\\\

ivo
źródło
7
To tylko rodzica CWD, a nie rodzica arbitralnej ścieżki, jak poprosił OP.
Sergio
Dodaj podwójne kropki na końcu swojego adresu URL, a zadziała np. os.path.abspath(r'E:\O3M_Tests_Embedded\branches\sw_test_level_gp\test_scripts\..\..') Wynik:E:\\O3M_Tests_Embedded\\branches
Arindam Roychowdhury
Oznacza to, że: /.
loretoparisi
26

Alternatywne rozwiązanie @kender

import os
os.path.dirname(os.path.normpath(yourpath))

gdzie yourpathjest ścieżka, dla której chcesz nadrzędny.

Ale to rozwiązanie nie jest idealne, ponieważ nie poradzi sobie w przypadku, gdy yourpath jest pusty ciąg lub kropka.

To inne rozwiązanie ładniej poradzi sobie z tym narożnym futerałem:

import os
os.path.normpath(os.path.join(yourpath, os.pardir))

Tutaj dane wyjściowe dla każdego znalezionego przypadku (ścieżka wejściowa jest względna):

os.path.dirname(os.path.normpath('a/b/'))          => 'a'
os.path.normpath(os.path.join('a/b/', os.pardir))  => 'a'

os.path.dirname(os.path.normpath('a/b'))           => 'a'
os.path.normpath(os.path.join('a/b', os.pardir))   => 'a'

os.path.dirname(os.path.normpath('a/'))            => ''
os.path.normpath(os.path.join('a/', os.pardir))    => '.'

os.path.dirname(os.path.normpath('a'))             => ''
os.path.normpath(os.path.join('a', os.pardir))     => '.'

os.path.dirname(os.path.normpath('.'))             => ''
os.path.normpath(os.path.join('.', os.pardir))     => '..'

os.path.dirname(os.path.normpath(''))              => ''
os.path.normpath(os.path.join('', os.pardir))      => '..'

os.path.dirname(os.path.normpath('..'))            => ''
os.path.normpath(os.path.join('..', os.pardir))    => '../..'

Ścieżka wejściowa jest bezwzględna (ścieżka systemu Linux):

os.path.dirname(os.path.normpath('/a/b'))          => '/a'
os.path.normpath(os.path.join('/a/b', os.pardir))  => '/a'

os.path.dirname(os.path.normpath('/a'))            => '/'
os.path.normpath(os.path.join('/a', os.pardir))    => '/'

os.path.dirname(os.path.normpath('/'))             => '/'
os.path.normpath(os.path.join('/', os.pardir))     => '/'
benjarobin
źródło
Normalizacja ścieżki jest zawsze dobrą praktyką, szczególnie podczas pracy na różnych platformach.
DevPlayer
To poprawna odpowiedź! Utrzymuje względne ścieżki względne. Dzięki!
Maxim
@Maxim To rozwiązanie nie było idealne, poprawiłem je, ponieważ oryginalne rozwiązanie nie obsługuje jednego przypadku
benjarobin
@benjarobin Tak, nie myślałem o sprawie narożnej. Dzięki.
Maxim
18
os.path.split(os.path.abspath(mydir))[0]
Dan Menes
źródło
To nie zadziała dla ścieżek do katalogu, po prostu zwróci katalog ponownie.
Anthony Briggs,
2
@AnthonyBriggs, właśnie wypróbowałem to za pomocą Pythona 2.7.3 na Ubuntu 12.04 i wydaje się, że działa dobrze. os.path.split(os.path.abspath("this/is/a/dir/"))[0]zwraca '/home/daniel/this/is/a'zgodnie z oczekiwaniami. W tej chwili nie mam działającego pola Windows, aby to sprawdzić. W jakiej konfiguracji zaobserwowałeś zgłaszane zachowanie?
Dan Menes
Można to zrobić parentdir = os.path.split(os.path.apspath(dir[:-1]))[0]. To - jestem pewien - działa, ponieważ jeśli na końcu jest ukośnik, jest on usuwany; jeśli nie ma ukośnika, to nadal będzie działać (nawet jeśli ostatnia część ścieżki ma tylko jeden znak długości) z powodu poprzedniego ukośnika. Zakłada to oczywiście, że ścieżka jest poprawna i nie mówi czegoś takiego /a//b/c///d////(w unixie jest to nadal poprawne), co w większości przypadków jest (właściwe), zwłaszcza gdy wykonujesz coś podobnego os.path.abspathlub jakąkolwiek inną os.pathfunkcję.
dylnmc
Ponadto, aby przeciwdziałać wielu ukośnikom na końcu, możesz po prostu napisać małą pętlę for, która je usuwa. Jestem pewien, że może istnieć nawet sprytny liniowiec, który mógłby to zrobić, a może to zrobić i os.path.split w jednej linii.
dylnmc
@Den Menes Właśnie zobaczyłem twój komentarz. Nie działa, jeśli masz coś takiego os.path.split("a/b//c/d///"), jak na przykład cd //////dev////// is equivalent to cd / dev / `or cd /dev; wszystkie są ważne w systemie Linux. Właśnie pojawił się z tym i może to być przydatne, ale: os.path.split(path[:tuple(ind for ind, char in enumerate(path) if char != "/" and char != "\\")[-1]])[0]. (To w zasadzie wyszukuje ostatni nie-ukośnik i pobiera podciąg ścieżki do tego znaku.) Użyłem, path = "/a//b///c///d////"a następnie uruchomiłem wyżej wymienioną instrukcję i otrzymałem '/a//b///c'.
dylnmc
14
os.path.abspath(os.path.join(somepath, '..'))

Przestrzegać:

import posixpath
import ntpath

print ntpath.abspath(ntpath.join('C:\\', '..'))
print ntpath.abspath(ntpath.join('C:\\foo', '..'))
print posixpath.abspath(posixpath.join('/', '..'))
print posixpath.abspath(posixpath.join('/home', '..'))
Ignacio Vazquez-Abrams
źródło
7
import os
print"------------------------------------------------------------"
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
print("example 1: "+SITE_ROOT)
PARENT_ROOT=os.path.abspath(os.path.join(SITE_ROOT, os.pardir))
print("example 2: "+PARENT_ROOT)
GRANDPAPA_ROOT=os.path.abspath(os.path.join(PARENT_ROOT, os.pardir))
print("example 3: "+GRANDPAPA_ROOT)
print "------------------------------------------------------------"
Dziadunio
źródło
6

Jeśli chcesz tylko na nazwę folderu, który jest bezpośrednim rodzicem pliku przewidzianego jako argument a nie na bezwzględnej ścieżki do tego pliku:

os.path.split(os.path.dirname(currentDir))[1]

tj. o currentDirwartości/home/user/path/to/myfile/file.ext

Powyższe polecenie zwróci:

myfile

8bitjunkie
źródło
3
os.path.basename (os.path.dirname (aktualny_katalog)) również tutaj działa.
DevPlayer
4
>>> import os
>>> os.path.basename(os.path.dirname(<your_path>))

Na przykład w Ubuntu:

>>> my_path = '/home/user/documents'
>>> os.path.basename(os.path.dirname(my_path))
# Output: 'user'

Na przykład w systemie Windows:

>>> my_path = 'C:\WINDOWS\system32'
>>> os.path.basename(os.path.dirname(my_path))
# Output: 'WINDOWS'

Oba przykłady wypróbowane w Pythonie 2.7

Soumendra
źródło
3
import os.path

os.path.abspath(os.pardir)
Washington Botelho
źródło
Zakłada się, że chcesz, aby katalog nadrzędny „bieżącego katalogu roboczego”, a nie katalog nadrzędny, zawiera ogólnie ścieżkę.
DevPlayer
3

Załóżmy, że mamy taką strukturę katalogów

1]

/home/User/P/Q/R

Chcemy uzyskać dostęp do ścieżki „P” z katalogu R, a następnie możemy uzyskać dostęp za pomocą

ROOT = os.path.abspath(os.path.join("..", os.pardir));

2]

/home/User/P/Q/R

Chcemy uzyskać dostęp do ścieżki katalogu „Q” z katalogu R, a następnie możemy uzyskać dostęp za pomocą

ROOT = os.path.abspath(os.path.join(".", os.pardir));
Rakesh Chaudhari
źródło
2

Po prostu dodając coś do odpowiedzi Tunga (musisz użyć, rstrip('/')aby być bardziej bezpiecznym, jeśli korzystasz z unixa).

>>> input = "../data/replies/"
>>> os.path.dirname(input.rstrip('/'))
'../data'
>>> input = "../data/replies"
>>> os.path.dirname(input.rstrip('/'))
'../data'

Ale jeśli nie używasz rstrip('/'), biorąc pod uwagę twój wkład jest

>>> input = "../data/replies/"

wyszedłby

>>> os.path.dirname(input)
'../data/replies'

co prawdopodobnie nie jest tym, na co patrzysz, ponieważ chcesz obu "../data/replies/"i "../data/replies"zachowywać się w ten sam sposób.

samsamara
źródło
1
Poleciłbym nie używać „danych wejściowych” jako zmiennej / odwołania. Jest to wbudowana funkcja.
DevPlayer
2
import os

dir_path = os.path.dirname(os.path.realpath(__file__))
parent_path = os.path.abspath(os.path.join(dir_path, os.pardir))
Miguel Mota
źródło
1
print os.path.abspath(os.path.join(os.getcwd(), os.path.pardir))

Możesz go użyć, aby uzyskać katalog nadrzędny bieżącej lokalizacji pliku py.

Eros Nikolli
źródło
2
Ta sugestia często prowadzi do błędów. os.getcwd () często NIE jest tam, gdzie znajduje się „twój plik py”. Pomyśl o pakietach. Jeśli „zaimportuję jakiś_pakiet_z_pakietami”, wiele modułów nie będzie w najwyższym katalogu tego pakietu. os.getcwd () zwraca miejsce, w którym wykonuje się najwyższy skrypt. I to również zakłada, że ​​robisz to z wiersza poleceń.
DevPlayer
0

POBIERZ ścieżkę katalogu nadrzędnego i utwórz nowy katalog (nazwę new_dir)

Uzyskaj ścieżkę katalogu nadrzędnego

os.path.abspath('..')
os.pardir

Przykład 1

import os
print os.makedirs(os.path.join(os.path.dirname(__file__), os.pardir, 'new_dir'))

Przykład 2

import os
print os.makedirs(os.path.join(os.path.dirname(__file__), os.path.abspath('..'), 'new_dir'))
Jay Patel
źródło
0
os.path.abspath('D:\Dir1\Dir2\..')

>>> 'D:\Dir1'

Więc ..pomaga

Arindam Roychowdhury
źródło
0
import os

def parent_filedir(n):
    return parent_filedir_iter(n, os.path.dirname(__file__))

def parent_filedir_iter(n, path):
    n = int(n)
    if n <= 1:
        return path
    return parent_filedir_iter(n - 1, os.path.dirname(path))

test_dir = os.path.abspath(parent_filedir(2))
fuyunliu
źródło
0

Wszystkie powyższe odpowiedzi doskonale nadają się do przejścia o jeden lub dwa poziomy katalogów, ale mogą być nieco kłopotliwe, jeśli trzeba przejrzeć drzewo katalogów o wiele poziomów (powiedzmy 5 lub 10). Można to zrobić zwięźle, dołączając do listy N os.pardirs w os.path.join. Przykład:

import os
# Create list of ".." times 5
upup = [os.pardir]*5
# Extract list as arguments of join()
go_upup = os.path.join(*upup)
# Get abspath for current file
up_dir = os.path.abspath(os.path.join(__file__, go_upup))
MPA
źródło