Reputation: 10845
Is there a way to return a list of all the subdirectories in the current directory in Python?
I know you can do this with files, but I need to get the list of directories instead.
Upvotes: 1002
Views: 1671962
Reputation: 165
I found that this works:
from pathlib import Path
start = '.' # put any starting point here
Path(start).glob('**/*/')
The */
matches directories at the end of any path **/
. If I wanted only the directories in the current folder, I can use:
Path(start).glob('*/')
Either way, I get a flat list of subdirectories.
Upvotes: 1
Reputation: 714
A lot of responses! After reviewing all the suggestions, I filtered out three candidates for listing all the folders in the tree and two methods for listing the immediate folders.
Every folder:
dirs_rglob = [x for x in folder.rglob('*') if x.is_dir()]
dirs_walk = [x[0] for x in os.walk(folder)]
dirs_custom = fast_scandir(folder)
where fast_scandir()
is a custom function suggested by user136036, see my code below. I tested the performance with the following Python code:
from pathlib import Path
from os import walk,scandir
from time import monotonic
#user136036 custom code:
def fast_scandir(dirname):
subfolders= [f.path for f in scandir(dirname) if f.is_dir()]
for dirname in list(subfolders):
subfolders.extend(fast_scandir(dirname))
return subfolders
folder = Path('c:/xampp/htdocs/fonts') # Insert your path here
msg = 'Using {}, seconds: {}, number of folders: {}.\n'
start = monotonic()
dirs_rglob = [x for x in folder.rglob('*') if x.is_dir()]
print(msg.format('Path.rglob', monotonic() - start, len(dirs_rglob)))
start = monotonic()
dirs_walk = [x[0] for x in walk(folder)]
print(msg.format('os.walk', monotonic() - start, len(dirs_walk)))
start = monotonic()
dirs_custom = fast_scandir(folder)
print(msg.format('fast_scandir', monotonic() - start, len(dirs_custom)))
As stated by user136036, his custom method is the fastest, but not much compared to os.walk(folder)
, while folder.rglob('*')
is really very slow. Since the custom method is not that much faster, I use os.walk(folder)
, since the latter function has no problems with the Windows operating system for system folders or the Recycle Bin.
If you only need a list of immediate folders, you can use
dirs_iter = [ f for f in folder.iterdir() if f.is_dir()]
dirs_scan = [ f.path for f in os.scandir(folder) if f.is_dir()]
The elapsed times are both very small, with the folder.iterdir()
method sometimes being slightly faster.
Bottom line: Use os.walk(folder)
for the entire tree of subdirectories and folder.iterdir()
or os.scandir(folder)
for the immediate subdirectories.
Upvotes: 3
Reputation: 431
This function, with a given parent directory
iterates over all its directories
recursively and prints
all the filenames
which it founds inside. Quite useful.
import os
def printDirectoryFiles(directory):
for filename in os.listdir(directory):
full_path=os.path.join(directory, filename)
if not os.path.isdir(full_path):
print( full_path + "\n")
def checkFolders(directory):
dir_list = next(os.walk(directory))[1]
#print(dir_list)
for dir in dir_list:
print(dir)
checkFolders(directory +"/"+ dir)
printDirectoryFiles(directory)
main_dir="C:/Users/S0082448/Desktop/carpeta1"
checkFolders(main_dir)
input("Press enter to exit ;")
Upvotes: 3
Reputation: 519
it's simple recursive solution for it
import os
def fn(dir=r"C:\Users\aryan\Downloads\opendatakit"): # 1.Get file names from directory
file_list = os.listdir(dir)
res = []
# print(file_list)
for file in file_list:
if os.path.isfile(os.path.join(dir, file)):
res.append(file)
else:
result = fn(os.path.join(dir, file))
if result:
res.extend(fn(os.path.join(dir, file)))
return res
res = fn()
print(res)
print(len(res))
Upvotes: 3
Reputation: 909
For anyone like me who just needed the names of the immediate folders within a directory this worked on Windows.
import os
for f in os.scandir(mypath):
print(f.name)
Upvotes: 5
Reputation: 894
import os
path = "test/"
files = [x[0] + "/" + y for x in os.walk(path) if len(x[-1]) > 0 for y in x[-1]]
Upvotes: 0
Reputation: 3477
You could just use glob.glob
from glob import glob
glob("/path/to/directory/*/", recursive = True)
Don't forget the trailing /
after the *
.
Upvotes: 332
Reputation: 161
using os walk
sub_folders = []
for dir, sub_dirs, files in os.walk(test_folder):
sub_folders.extend(sub_dirs)
Upvotes: 7
Reputation: 413
print("\nWe are listing out only the directories in current directory -")
directories_in_curdir = list(filter(os.path.isdir, os.listdir(os.curdir)))
print(directories_in_curdir)
files = list(filter(os.path.isfile, os.listdir(os.curdir)))
print("\nThe following are the list of all files in the current directory -")
print(files)
Upvotes: 36
Reputation: 549
This below class would be able to get list of files, folder and all sub folder inside a given directory
import os
import json
class GetDirectoryList():
def __init__(self, path):
self.main_path = path
self.absolute_path = []
self.relative_path = []
def get_files_and_folders(self, resp, path):
all = os.listdir(path)
resp["files"] = []
for file_folder in all:
if file_folder != "." and file_folder != "..":
if os.path.isdir(path + "/" + file_folder):
resp[file_folder] = {}
self.get_files_and_folders(resp=resp[file_folder], path= path + "/" + file_folder)
else:
resp["files"].append(file_folder)
self.absolute_path.append(path.replace(self.main_path + "/", "") + "/" + file_folder)
self.relative_path.append(path + "/" + file_folder)
return resp, self.relative_path, self.absolute_path
@property
def get_all_files_folder(self):
self.resp = {self.main_path: {}}
all = self.get_files_and_folders(self.resp[self.main_path], self.main_path)
return all
if __name__ == '__main__':
mylib = GetDirectoryList(path="sample_folder")
file_list = mylib.get_all_files_folder
print (json.dumps(file_list))
Whereas Sample Directory looks like
sample_folder/
lib_a/
lib_c/
lib_e/
__init__.py
a.txt
__init__.py
b.txt
c.txt
lib_d/
__init__.py
__init__.py
d.txt
lib_b/
__init__.py
e.txt
__init__.py
Result Obtained
[
{
"files": [
"__init__.py"
],
"lib_b": {
"files": [
"__init__.py",
"e.txt"
]
},
"lib_a": {
"files": [
"__init__.py",
"d.txt"
],
"lib_c": {
"files": [
"__init__.py",
"c.txt",
"b.txt"
],
"lib_e": {
"files": [
"__init__.py",
"a.txt"
]
}
},
"lib_d": {
"files": [
"__init__.py"
]
}
}
},
[
"sample_folder/lib_b/__init__.py",
"sample_folder/lib_b/e.txt",
"sample_folder/__init__.py",
"sample_folder/lib_a/lib_c/lib_e/__init__.py",
"sample_folder/lib_a/lib_c/lib_e/a.txt",
"sample_folder/lib_a/lib_c/__init__.py",
"sample_folder/lib_a/lib_c/c.txt",
"sample_folder/lib_a/lib_c/b.txt",
"sample_folder/lib_a/lib_d/__init__.py",
"sample_folder/lib_a/__init__.py",
"sample_folder/lib_a/d.txt"
],
[
"lib_b/__init__.py",
"lib_b/e.txt",
"sample_folder/__init__.py",
"lib_a/lib_c/lib_e/__init__.py",
"lib_a/lib_c/lib_e/a.txt",
"lib_a/lib_c/__init__.py",
"lib_a/lib_c/c.txt",
"lib_a/lib_c/b.txt",
"lib_a/lib_d/__init__.py",
"lib_a/__init__.py",
"lib_a/d.txt"
]
]
Upvotes: 0
Reputation: 356
This should work, as it also creates a directory tree;
import os
import pathlib
def tree(directory):
print(f'+ {directory}')
print("There are " + str(len(os.listdir(os.getcwd()))) + \
" folders in this directory;")
for path in sorted(directory.glob('*')):
depth = len(path.relative_to(directory).parts)
spacer = ' ' * depth
print(f'{spacer}+ {path.name}')
This should list all the directories in a folder using the pathlib
library. path.relative_to(directory).parts
gets the elements relative to the current working dir.
Upvotes: 0
Reputation: 2797
The easiest way:
from pathlib import Path
from glob import glob
current_dir = Path.cwd()
all_sub_dir_paths = glob(str(current_dir) + '/*/') # returns list of sub directory paths
all_sub_dir_names = [Path(sub_dir).name for sub_dir in all_sub_dir_paths]
Upvotes: 8
Reputation: 1957
Lot of nice answers out there but if you came here looking for a simple way to get list of all files or folders at once. You can take advantage of the os offered find on linux and mac which and is much faster than os.walk
import os
all_files_list = os.popen("find path/to/my_base_folder -type f").read().splitlines()
all_sub_directories_list = os.popen("find path/to/my_base_folder -type d").read().splitlines()
OR
import os
def get_files(path):
all_files_list = os.popen(f"find {path} -type f").read().splitlines()
return all_files_list
def get_sub_folders(path):
all_sub_directories_list = os.popen(f"find {path} -type d").read().splitlines()
return all_sub_directories_list
Upvotes: 3
Reputation: 49054
Python 3.4 introduced the pathlib
module into the standard library, which provides an object oriented approach to handle filesystem paths:
from pathlib import Path
p = Path('./')
# All subdirectories in the current directory, not recursive.
[f for f in p.iterdir() if f.is_dir()]
To recursively list all subdirectories, path globbing can be used with the **
pattern.
# This will also include the current directory '.'
list(p.glob('**'))
Note that a single *
as the glob pattern would include both files and directories non-recursively. To get only directories, a trailing /
can be appended but this only works when using the glob library directly, not when using glob via pathlib:
import glob
# These three lines return both files and directories
list(p.glob('*'))
list(p.glob('*/'))
glob.glob('*')
# Whereas this returns only directories
glob.glob('*/')
So Path('./').glob('**')
matches the same paths as glob.glob('**/', recursive=True)
.
Pathlib is also available on Python 2.7 via the pathlib2 module on PyPi.
Upvotes: 99
Reputation: 1403
By joining multiple solutions from here, this is what I ended up using:
import os
import glob
def list_dirs(path):
return [os.path.basename(x) for x in filter(
os.path.isdir, glob.glob(os.path.join(path, '*')))]
Upvotes: 3
Reputation: 12364
Much nicer than the above, because you don't need several os.path.join() and you will get the full path directly (if you wish), you can do this in Python 3.5 and above.
subfolders = [ f.path for f in os.scandir(folder) if f.is_dir() ]
This will give the complete path to the subdirectory.
If you only want the name of the subdirectory use f.name
instead of f.path
https://docs.python.org/3/library/os.html#os.scandir
Slightly OT: In case you need all subfolder recursively and/or all files recursively, have a look at this function, that is faster than os.walk
& glob
and will return a list of all subfolders as well as all files inside those (sub-)subfolders: https://stackoverflow.com/a/59803793/2441026
In case you want only all subfolders recursively:
def fast_scandir(dirname):
subfolders= [f.path for f in os.scandir(dirname) if f.is_dir()]
for dirname in list(subfolders):
subfolders.extend(fast_scandir(dirname))
return subfolders
Returns a list of all subfolders with their full paths. This again is faster than os.walk
and a lot faster than glob
.
An analysis of all functions
tl;dr:
- If you want to get all immediate subdirectories for a folder use os.scandir
.
- If you want to get all subdirectories, even nested ones, use os.walk
or - slightly faster - the fast_scandir
function above.
- Never use os.walk
for only top-level subdirectories, as it can be hundreds(!) of times slower than os.scandir
.
os.walk
will be the base folder. So you will not get only subdirectories. You can use fu.pop(0)
to remove it.Results:
os.scandir took 1 ms. Found dirs: 439
os.walk took 463 ms. Found dirs: 441 -> it found the nested one + base folder.
glob.glob took 20 ms. Found dirs: 439
pathlib.iterdir took 18 ms. Found dirs: 439
os.listdir took 18 ms. Found dirs: 439
Tested with W7x64, Python 3.8.1.
# -*- coding: utf-8 -*-
# Python 3
import time
import os
from glob import glob
from pathlib import Path
directory = r"<insert_folder>"
RUNS = 1
def run_os_walk():
a = time.time_ns()
for i in range(RUNS):
fu = [x[0] for x in os.walk(directory)]
print(f"os.walk\t\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")
def run_glob():
a = time.time_ns()
for i in range(RUNS):
fu = glob(directory + "/*/")
print(f"glob.glob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")
def run_pathlib_iterdir():
a = time.time_ns()
for i in range(RUNS):
dirname = Path(directory)
fu = [f for f in dirname.iterdir() if f.is_dir()]
print(f"pathlib.iterdir\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")
def run_os_listdir():
a = time.time_ns()
for i in range(RUNS):
dirname = Path(directory)
fu = [os.path.join(directory, o) for o in os.listdir(directory) if os.path.isdir(os.path.join(directory, o))]
print(f"os.listdir\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found dirs: {len(fu)}")
def run_os_scandir():
a = time.time_ns()
for i in range(RUNS):
fu = [f.path for f in os.scandir(directory) if f.is_dir()]
print(f"os.scandir\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms.\tFound dirs: {len(fu)}")
if __name__ == '__main__':
run_os_scandir()
run_os_walk()
run_glob()
run_pathlib_iterdir()
run_os_listdir()
Upvotes: 345
Reputation: 31
we can get list of all the folders by using os.walk()
import os
path = os.getcwd()
pathObject = os.walk(path)
this pathObject is a object and we can get an array by
arr = [x for x in pathObject]
arr is of type [('current directory', [array of folder in current directory], [files in current directory]),('subdirectory', [array of folder in subdirectory], [files in subdirectory]) ....]
We can get list of all the subdirectory by iterating through the arr and printing the middle array
for i in arr:
for j in i[1]:
print(j)
This will print all the subdirectory.
To get all the files:
for i in arr:
for j in i[2]:
print(i[0] + "/" + j)
Upvotes: 3
Reputation: 2731
This is how I do it.
import os
for x in os.listdir(os.getcwd()):
if os.path.isdir(x):
print(x)
Upvotes: 11
Reputation: 76
Function to return a List of all subdirectories within a given file path. Will search through the entire file tree.
import os
def get_sub_directory_paths(start_directory, sub_directories):
"""
This method iterates through all subdirectory paths of a given
directory to collect all directory paths.
:param start_directory: The starting directory path.
:param sub_directories: A List that all subdirectory paths will be
stored to.
:return: A List of all sub-directory paths.
"""
for item in os.listdir(start_directory):
full_path = os.path.join(start_directory, item)
if os.path.isdir(full_path):
sub_directories.append(full_path)
# Recursive call to search through all subdirectories.
get_sub_directory_paths(full_path, sub_directories)
return sub_directories
Upvotes: 4
Reputation: 2080
This will list all subdirectories right down the file tree.
import pathlib
def list_dir(dir):
path = pathlib.Path(dir)
dir = []
try:
for item in path.iterdir():
if item.is_dir():
dir.append(item)
dir = dir + list_dir(item)
return dir
except FileNotFoundError:
print('Invalid directory')
pathlib
is new in version 3.4
Upvotes: 4
Reputation: 14910
Copy paste friendly in ipython
:
import os
d='.'
folders = list(filter(lambda x: os.path.isdir(os.path.join(d, x)), os.listdir(d)))
Output from print(folders)
:
['folderA', 'folderB']
Upvotes: 13
Reputation: 5599
With full path and accounting for path being .
, ..
, \\
, ..\\..\\subfolder
, etc:
import os, pprint
pprint.pprint([os.path.join(os.path.abspath(path), x[0]) \
for x in os.walk(os.path.abspath(path))])
Upvotes: 8
Reputation: 1232
I've had a similar question recently, and I found out that the best answer for python 3.6 (as user havlock added) is to use os.scandir
. Since it seems there is no solution using it, I'll add my own. First, a non-recursive solution that lists only the subdirectories directly under the root directory.
def get_dirlist(rootdir):
dirlist = []
with os.scandir(rootdir) as rit:
for entry in rit:
if not entry.name.startswith('.') and entry.is_dir():
dirlist.append(entry.path)
dirlist.sort() # Optional, in case you want sorted directory names
return dirlist
The recursive version would look like this:
def get_dirlist(rootdir):
dirlist = []
with os.scandir(rootdir) as rit:
for entry in rit:
if not entry.name.startswith('.') and entry.is_dir():
dirlist.append(entry.path)
dirlist += get_dirlist(entry.path)
dirlist.sort() # Optional, in case you want sorted directory names
return dirlist
keep in mind that entry.path
wields the absolute path to the subdirectory. In case you only need the folder name, you can use entry.name
instead. Refer to os.DirEntry for additional details about the entry
object.
Upvotes: 6
Reputation: 3730
Implemented this using python-os-walk. (http://www.pythonforbeginners.com/code-snippets-source-code/python-os-walk/)
import os
print("root prints out directories only from what you specified")
print("dirs prints out sub-directories from root")
print("files prints out all files from root and directories")
print("*" * 20)
for root, dirs, files in os.walk("/var/log"):
print(root)
print(dirs)
print(files)
Upvotes: 26
Reputation: 137502
import os
d = '.'
[os.path.join(d, o) for o in os.listdir(d)
if os.path.isdir(os.path.join(d,o))]
Upvotes: 217
Reputation: 22052
Here are a couple of simple functions based on @Blair Conrad's example -
import os
def get_subdirs(dir):
"Get a list of immediate subdirectories"
return next(os.walk(dir))[1]
def get_subfiles(dir):
"Get a list of immediate subfiles"
return next(os.walk(dir))[2]
Upvotes: 11
Reputation: 242030
Do you mean immediate subdirectories, or every directory right down the tree?
Either way, you could use os.walk
to do this:
os.walk(directory)
will yield a tuple for each subdirectory. Ths first entry in the 3-tuple is a directory name, so
[x[0] for x in os.walk(directory)]
should give you all of the subdirectories, recursively.
Note that the second entry in the tuple is the list of child directories of the entry in the first position, so you could use this instead, but it's not likely to save you much.
However, you could use it just to give you the immediate child directories:
next(os.walk('.'))[1]
Or see the other solutions already posted, using os.listdir
and os.path.isdir
, including those at "How to get all of the immediate subdirectories in Python".
Upvotes: 957
Reputation: 493
You can get the list of subdirectories (and files) in Python 2.7 using os.listdir(path)
import os
os.listdir(path) # list of subdirectories and files
Upvotes: 20
Reputation: 3969
This answer didn't seem to exist already.
directories = [ x for x in os.listdir('.') if os.path.isdir(x) ]
Upvotes: 6
Reputation: 4035
Although this question is answered a long time ago. I want to recommend to use the pathlib
module since this is a robust way to work on Windows and Unix OS.
So to get all paths in a specific directory including subdirectories:
from pathlib import Path
paths = list(Path('myhomefolder', 'folder').glob('**/*.txt'))
# all sorts of operations
file = paths[0]
file.name
file.stem
file.parent
file.suffix
etc.
Upvotes: 15