Is there a way to get functionality similar to
mkdir -p on the shell from within Python. I am looking for a solution other than a system call. I am sure the code is less than 20 lines, and I am wondering if someone has already written it?
I think Asa’s answer is essentially correct, but you could extend it a little to act more like
mkdir -p, either:
import os def mkdir_path(path): if not os.access(path, os.F_OK): os.mkdirs(path)
import os import errno def mkdir_path(path): try: os.mkdirs(path) except os.error, e: if e.errno != errno.EEXIST: raise
These both handle the case where the path already exists silently but let other errors bubble up.
For Python ≥ 3.5, use
import pathlib pathlib.Path("/tmp/path/to/desired/directory").mkdir(parents=True, exist_ok=True)
exist_ok parameter was added in Python 3.5.
For Python ≥ 3.2,
os.makedirs has an optional third argument
exist_ok that, when
True, enables the
mkdir -p functionality—unless
mode is provided and the existing directory has different permissions than the intended ones; in that case,
OSError is raised as previously:
import os os.makedirs("/tmp/path/to/desired/directory", exist_ok=True)
For even older versions of Python, you can use
os.makedirs and ignore the error:
import errno import os def mkdir_p(path): try: os.makedirs(path) except OSError as exc: # Python ≥ 2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass # possibly handle other errno cases here, otherwise finally: else: raise
mkdir -p gives you an error if the file already exists:
$ touch /tmp/foo $ mkdir -p /tmp/foo mkdir: cannot create directory `/tmp/foo': File exists
So a refinement to the previous suggestions would be to re-
raise the exception if
False (when checking for
(Update) See also this highly similar question; I agree with the accepted answer (and caveats) except I would recommend
os.path.isdir instead of
(Update) Per a suggestion in the comments, the full function would look like:
import os def mkdirp(directory): if not os.path.isdir(directory): os.makedirs(directory)
This is easier than trapping the exception:
import os if not os.path.exists(...): os.makedirs(...)
Disclaimer This approach requires two system calls which is more susceptible to race conditions under certain environments/conditions. If you’re writing something more sophisticated than a simple throwaway script running in a controlled environment, you’re better off going with the accepted answer that requires only one system call.
I’m tempted to delete this answer, but I think there’s value in the comment thread below. As such, I’m converting it to a wiki.
In Python >=3.2, that’s
In earlier versions, use @tzot’s answer.
import os import tempfile path = tempfile.mktemp(dir=path) os.makedirs(path) os.rmdir(path)
As mentioned in the other solutions, we want to be able to hit the file system once while mimicking the behaviour of
mkdir -p. I don’t think that this is possible to do, but we should get as close as possible.
Code first, explanation later:
import os import errno def mkdir_p(path): """ 'mkdir -p' in Python """ try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise
As the comments to @tzot’s answer indicate there are problems with checking whether you can create a directory before you actually create it: you can’t tell whether someone has changed the file system in the meantime. That also fits in with Python’s style of asking for forgiveness, not permission.
So the first thing we should do is try to make the directory, then if it goes wrong, work out why.
As Jacob Gabrielson points out, one of the cases we must look for is the case where a file already exists where we are trying to put the directory.
$ touch /tmp/foo $ mkdir -p /tmp/foo mkdir: cannot create directory '/tmp/foo': File exists
The analogous behaviour in Python would be to raise an exception.
So we have to work out if this was the case. Unfortunately, we can’t. We get the same error message back from makedirs whether a directory exists (good) or a file exists preventing the creation of the directory (bad).
The only way to work out what happened is to inspect the file system again to see if there is a directory there. If there is, then return silently, otherwise raise the exception.
The only problem is that the file system may be in a different state now than when makedirs was called. eg: a file existed causing makedirs to fail, but now a directory is in its place. That doesn’t really matter that much, because the the function will only exit silently without raising an exception when at the time of the last file system call the directory existed.
Recently, I found this distutils.dir_util.mkpath:
In : from distutils.dir_util import mkpath In : mkpath('./foo/bar') Out: ['foo', 'foo/bar']
import os def mkdir_p(filename): try: folder=os.path.dirname(filename) if not os.path.exists(folder): os.makedirs(folder) return True except: return False
filename = "./download/80c16ee665c8/upload/backup/mysql/2014-12-22/adclient_sql_2014-12-22-13-38.sql.gz" if (mkdir_p(filename): print "Created dir :%s" % (os.path.dirname(filename))
With Pathlib from python3 standard library:
If parents is true, any missing parents of this path are created as
needed; they are created with the default permissions without taking
mode into account (mimicking the POSIX mkdir -p command).
If exist_ok is false (the default), an FileExistsError is raised if
the target directory already exists.
If exist_ok is true, FileExistsError exceptions will be ignored (same
behavior as the POSIX mkdir -p command), but only if the last path
component is not an existing non-directory file.
Changed in version 3.5: The exist_ok parameter was added.
I’ve had success with the following personally, but my function should probably be called something like ‘ensure this directory exists’:
def mkdirRecursive(dirpath): import os if os.path.isdir(dirpath): return h,t = os.path.split(dirpath) # head/tail if not os.path.isdir(h): mkdirRecursive(h) os.mkdir(join(h,t)) # end mkdirRecursive
import os from os.path import join as join_paths def mk_dir_recursive(dir_path): if os.path.isdir(dir_path): return h, t = os.path.split(dir_path) # head/tail if not os.path.isdir(h): mk_dir_recursive(h) new_path = join_paths(h, t) if not os.path.isdir(new_path): os.mkdir(new_path)
based on @Dave C’s answer but with a bug fixed where part of the tree already exists