530

Could someone tell me how to get the parent directory of a path in Python in a cross platform way. E.g.

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

and

C:\ ---> C:\

If the directory doesn't have a parent directory, it returns the directory itself. The question might seem simple but I couldn't dig it up through Google.

Mridang Agarwalla
  • 43,201
  • 71
  • 221
  • 382

21 Answers21

756

Python 3.4

Use the pathlib module.

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

Old answer

Try this:

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

where yourpath is the path you want the parent for.

Neuron
  • 5,141
  • 5
  • 38
  • 59
kender
  • 85,663
  • 26
  • 103
  • 145
  • 172
    Your answer is correct but convoluted; `os.path.dirname` is the function for this, like `a+=5-4` is more convoluted than `a+=1`. The question requested only the parent directory, not whether is exists or the *true* parent directory assuming symbolic links get in the way. – tzot May 24 '10 at 12:03
  • 2
    I tried to change the code snippet to `os.pardir`, but unfortunately Stack Overflow "Edits must be at least 6 non-space characters". It seems smart enough to detect whitespace padding; maybe the OP can correct this someday... – monsur Aug 09 '12 at 15:27
  • 56
    @tzot: unfortunately `os.path.dirname` gives different results depending on whether a trailing slash is included in the path. If you want reliable results you need to use the `os.path.join` method in answer above. – Artfunkel Jun 28 '13 at 10:32
  • 4
    @Artfunkel Thanks. Your comment should be appended in the answer for its completeness' sake. – tzot Jun 29 '13 at 08:18
  • @tzot: I guess the OP wanted to know how to move from one subdirectory to its parent. But `dirname` only returns the directory part of the full pathname to a *file* by removing the last component auf the path string (here: `..`/`pardir`) via `split`. `abspath` on the other hand correctly interprets and resolves `..` as a reference to the top-level directory, which, I guess, is what the OP really asked for. – Frank May 27 '15 at 08:45
  • 27
    Since this is apparently complicated enough to warrant a StackOverflow question, I feel that this should be added to the os.path library as a built-in function. – antred Mar 04 '16 at 12:44
  • Don't miss @benjarobin's solution below if you are interested to keep relative path relative. – Maxim Oct 24 '17 at 19:10
  • 1
    This doesn't work if you are trying to work with paths on a different file system – alta Aug 24 '18 at 01:56
  • 1
    Try `from pathlib import Path` and next `print((Path().parent.absolute()).parent)` – Artem S. Zhelonkin Nov 16 '20 at 17:04
  • `Path("/here/your/path/file.txt")` does not work. Error message: `'WindowsPath' object is not callable` – mihca Aug 31 '21 at 13:18
  • I prefer ```os.path.dirname``` since it gave me the path in linux format when converting back to string. May not matter for most people but the Pathlib way gave me it back in stinky windows format on my pc – brando f Mar 03 '23 at 14:35
  • Works fine. print(os.path.abspath(os.path.join(yourpath, os.pardir))) No need 'pathlib', enough os packages. – muthukumar Apr 11 '23 at 07:14
395

Using os.path.dirname:

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

Caveat: os.path.dirname() gives different results depending on whether a trailing slash is included in the path. This may or may not be the semantics you want. Cf. @kender's answer using os.path.join(yourpath, os.pardir).

LarsH
  • 27,481
  • 8
  • 94
  • 152
Wai Yip Tung
  • 18,106
  • 10
  • 43
  • 47
  • 8
    `os.path.dirname(r'C:\Program Files')` what? Python's just giving you the directory where the file 'Program Files' would be. What's more, it doesn't even have to exist, behold: `os.path.dirname(r'c:\i\like\to\eat\pie')` outputs `'c:\\i\\like\\to\\eat'` – Nick T May 18 '10 at 19:28
  • 52
    The original poster does not state that the directory have to exist. There are a lot of pathname methods that does nothing but string manipulation. To verify if the pathname actually exist requires a disk access. Depends on the application this may or may not be desirable. – Wai Yip Tung May 18 '10 at 19:45
  • 1
    Seems that this would be a good solution if you knew that the input directory was valid (which can be checked). – new name May 18 '10 at 19:49
  • 1
    Most of the other provided solutions don't check if the parent exists either (as most os.path functions are indeed string manipulations _only_), so no big loss here. And all the other solutions here seem unnecessary complex to me, so I'm upvoting this one :) Btw, imho the os.path is one of the worst classes for python (especially implementation-wise -- platform-independence screwed up is where it hurted me most :( ) – riviera Aug 11 '12 at 23:43
  • 12
    this solution is sensitive to trailing os.sep. Say os.sep=='/'. dirname(foo/bar) -> foo, but dirname(foo/bar/) -> foo/bar – marcin Sep 10 '12 at 13:28
  • 9
    That's by design. It comes down to the interpretation of a path with a trailing /. Do you consider "path1" equals to "path1/"? The library use the most general interpretation that they are distinct. In some context people may want to treat them as equivalent. In this case you can do a rstrip('/') first. Had the library pick the other interpretation you will lost fidelity. – Wai Yip Tung Sep 11 '12 at 16:57
  • 1
    @WaiYipTung "Do you consider "path1" equals to "path1/"" --> Yes, because they **are**. In what context would you ever _not_ treat them as equivalent? – Ryan Jul 24 '14 at 06:52
  • 4
    @Ryan, I don't know about that. There is an entire RFC 1808 written to address the issue of relative path in URI and all the subtlety of the presence and absence of a trailing /. If you know of any documentation that says they should be treated equivalent in general please point it out. – Wai Yip Tung Jul 24 '14 at 15:12
  • 2
    @WaiYipTung Hmm, okay, seems silly but I thought this was just a library decision. Makes more sense then. – Ryan Jul 24 '14 at 16:41
160

The Pathlib method (Python 3.4+)

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

The traditional method

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


Which method should I use?

Use the traditional method if:

  • You are worried about existing code generating errors if it were to use a Pathlib object. (Since Pathlib objects cannot be concatenated with strings.)

  • Your Python version is less than 3.4.

  • You need a string, and you received a string. Say for example you have a string representing a filepath, and you want to get the parent directory so you can put it in a JSON string. It would be kind of silly to convert to a Pathlib object and back again for that.

If none of the above apply, use Pathlib.



What is Pathlib?

If you don't know what Pathlib is, the Pathlib module is a terrific module that makes working with files even easier for you. Most if not all of the built in Python modules that work with files will accept both Pathlib objects and strings. I've highlighted below a couple of examples from the Pathlib documentation that showcase some of the neat things you can do with Pathlib.

Navigating inside a directory tree:

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

Querying path properties:

>>> q.exists()
True
>>> q.is_dir()
False
hostingutilities.com
  • 8,894
  • 3
  • 41
  • 51
  • 4
    This is the only sane answer. If you're forced to use Python 2, just `pip install pathlib2` and use the backport. – Navin Nov 02 '17 at 05:51
  • 5
    This solution is NOT sensitive to trailing `os.sep`! – Dylan F Jun 18 '18 at 15:08
  • 1
    You don't really have to "worry about existing code generating errors if it were to use a Pathlib object" because you can just wrap the pathlib object: `path_as_string = str(Path())`. – John Nov 22 '20 at 21:33
47
import os
p = os.path.abspath('..')

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

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

Kenly
  • 24,317
  • 7
  • 44
  • 60
ivo
  • 4,101
  • 5
  • 33
  • 42
36

An alternate solution of @kender

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

where yourpath is the path you want the parent for.

But this solution is not perfect, since it will not handle the case where yourpath is an empty string, or a dot.

This other solution will handle more nicely this corner case:

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

Here the outputs for every case that can find (Input path is relative):

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))    => '../..'

Input path is absolute (Linux path):

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
  • 4,410
  • 27
  • 21
20
os.path.split(os.path.abspath(mydir))[0]
Dan Menes
  • 6,667
  • 1
  • 33
  • 35
  • This won't work for paths which are to a directory, it'll just return the directory again. – Anthony Briggs Feb 20 '13 at 02:37
  • 2
    @AnthonyBriggs, I just tried this using Python 2.7.3 on Ubuntu 12.04 and it seems to work fine. `os.path.split(os.path.abspath("this/is/a/dir/"))[0]` returns `'/home/daniel/this/is/a'` as expected. I don't at the moment have a running Windows box to check there. On what setup have you observed the behavior that you report? – Dan Menes Feb 22 '13 at 00:59
  • You could do `parentdir = os.path.split(os.path.apspath(dir[:-1]))[0]`. This - I am certain - works because if there is a slash on the end, then it is removed; if there is no slash, this will still work (even if the last part of the path is only one char long) because of the preceding slash. This of course assumes that the path is proper and not say something like `/a//b/c///d////` (in unix this is valid still), which in most cases they are (proper) especially when you do something like `os.path.abspath` or any other `os.path` function. – dylnmc Oct 04 '14 at 16:51
  • Also, to counteract a lot of slashes on the end, you could just write a small for loop that removes those. I'm sure there could even be a clever one-liner to do it, or maybe do that and os.path.split in one line. – dylnmc Oct 04 '14 at 16:57
  • @Den Menes I just saw you comment. It doesn't work if you have something like `os.path.split("a/b//c/d///")` and, for example, `cd //////dev////// is equivalent to `cd /dev/` or `cd /dev`; all of these are valid in linux. I just came up with this and it may be useful, though: `os.path.split(path[:tuple(ind for ind, char in enumerate(path) if char != "/" and char != "\\")[-1]])[0]`. (This essentially searches for the last non-slash, and gets the substring of the path up to that char.) I used `path = "/a//b///c///d////"` and then ran the aforementioned statement and got `'/a//b///c'`. – dylnmc Oct 04 '14 at 17:13
  • With the understanding that the line above is for demonstration, I'd still recommend to not use "dir" as a variable/reference. It is also a built-in function. – DevPlayer Jan 28 '16 at 13:48
16
os.path.abspath(os.path.join(somepath, '..'))

Observe:

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
  • 776,304
  • 153
  • 1,341
  • 1,358
10
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 "------------------------------------------------------------"
Grandpapa
  • 169
  • 1
  • 6
10
>>> import os
>>> os.path.basename(os.path.dirname(<your_path>))

For example in Ubuntu:

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

For example in Windows:

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

Both examples tried in Python 2.7

Soumendra
  • 1,518
  • 3
  • 27
  • 54
9

Suppose we have directory structure like

1]

/home/User/P/Q/R

We want to access the path of "P" from the directory R then we can access using

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

2]

/home/User/P/Q/R

We want to access the path of "Q" directory from the directory R then we can access using

ROOT = os.path.abspath(os.path.join(".", os.pardir));
Rakesh Chaudhari
  • 3,310
  • 1
  • 27
  • 25
6

If you want only the name of the folder that is the immediate parent of the file provided as an argument and not the absolute path to that file:

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

i.e. with a currentDir value of /home/user/path/to/myfile/file.ext

The above command will return:

myfile

8bitjunkie
  • 12,793
  • 9
  • 57
  • 70
4
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
  • 20,135
  • 5
  • 45
  • 64
3
import os.path

os.path.abspath(os.pardir)
  • This presumes you want the parent directory of "the current working directory" and not the parent directory any path in general. – DevPlayer Jan 28 '16 at 14:00
3

Just adding something to the Tung's answer (you need to use rstrip('/') to be more of the safer side if you're on a unix box).

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

But, if you don't use rstrip('/'), given your input is

>>> input1 = "../data/replies/"

would output,

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

which is probably not what you're looking at as you want both "../data/replies/" and "../data/replies" to behave the same way.

samsamara
  • 4,630
  • 7
  • 36
  • 66
2
print os.path.abspath(os.path.join(os.getcwd(), os.path.pardir))

You can use this to get the parent directory of the current location of your py file.

jofel
  • 3,297
  • 17
  • 31
Eros Nikolli
  • 1,011
  • 1
  • 7
  • 8
  • 3
    That suggestion often leads to bugs. os.getcwd() is often NOT where "your py file" is. Think packages. If I "import some_package_with_subpackages" many modules will not be in that package's top-most directory. os.getcwd() returns where you execute top-most script. And that also presumes you are doing it from a command line. – DevPlayer Jan 28 '16 at 13:56
  • Like DevPlayer noted os.getcwd() is not necessarily the location of your python file. sys.argv[0] is what you're looking for. – Anonymous1847 Jul 21 '20 at 01:08
1
import os 

def parent_directory():
  # Create a relative path to the parent of the current working directory 
  relative_parent = os.path.join(os.getcwd(), "..") # .. means parent directory

  # Return the absolute path of the parent directory
  return os.path.abspath(relative_parent)

print(parent_directory())
Nava Bogatee
  • 1,465
  • 13
  • 15
0

GET Parent Directory Path and make New directory (name new_dir)

Get Parent Directory Path

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

Example 1

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

Example 2

import os
print os.makedirs(os.path.join(os.path.dirname(__file__), os.path.abspath('..'), 'new_dir'))
Jaykumar Patel
  • 26,836
  • 12
  • 74
  • 76
0
os.path.abspath('D:\Dir1\Dir2\..')

>>> 'D:\Dir1'

So a .. helps

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Arindam Roychowdhury
  • 5,927
  • 5
  • 55
  • 63
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
  • 5
  • 2
0

The answers given above are all perfectly fine for going up one or two directory levels, but they may get a bit cumbersome if one needs to traverse the directory tree by many levels (say, 5 or 10). This can be done concisely by joining a list of N os.pardirs in os.path.join. Example:

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
  • 1,878
  • 2
  • 26
  • 51
0

To find the parent of the current working directory:

import pathlib
pathlib.Path().resolve().parent
Ondrej Sotolar
  • 1,352
  • 1
  • 19
  • 29