How to list only top level directories in Python?
Asked Answered
O

21

205

I want to be able to list only the directories inside some folder. This means I don't want filenames listed, nor do I want additional sub-folders.

Let's see if an example helps. In the current directory we have:

>>> os.listdir(os.getcwd())
['cx_Oracle-doc', 'DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'mod_p
ython-wininst.log', 'NEWS.txt', 'pymssql-wininst.log', 'python.exe', 'pythonw.ex
e', 'README.txt', 'Removemod_python.exe', 'Removepymssql.exe', 'Scripts', 'tcl',
 'Tools', 'w9xpopen.exe']

However, I don't want filenames listed. Nor do I want sub-folders such as \Lib\curses. Essentially what I want works with the following:

>>> for root, dirnames, filenames in os.walk('.'):
...     print dirnames
...     break
...
['cx_Oracle-doc', 'DLLs', 'Doc', 'include', 'Lib', 'libs', 'Scripts', 'tcl', 'Tools']

However, I'm wondering if there's a simpler way of achieving the same results. I get the impression that using os.walk only to return the top level is inefficient/too much.

Osterman answered 26/9, 2008 at 19:1 Comment(3)
os.walk() uses a generator function and would not be ineffective if you are using it just for top-level.Siegbahn
[d for d in a_path.iterdir() if d.is_dir] uses pathlib.PathPeripeteia
@DarrenWeber: I think it should be: [d for d in a_path.iterdir() if d.is_dir()]Oleoresin
U
173

Filter the result using os.path.isdir() (and use os.path.join() to get the real path):

>>> [ name for name in os.listdir(thedir) if os.path.isdir(os.path.join(thedir, name)) ]
['ctypes', 'distutils', 'encodings', 'lib-tk', 'config', 'idlelib', 'xml', 'bsddb', 'hotshot', 'logging', 'doc', 'test', 'compiler', 'curses', 'site-packages', 'email', 'sqlite3', 'lib-dynload', 'wsgiref', 'plat-linux2', 'plat-mac']
Undesigning answered 26/9, 2008 at 19:6 Comment(2)
This take a lot of processing vs very simple os.walk().next()[1]Neurophysiology
Doge, to easy! Had to use next(os.walk(dpath))[1] but same concept. Thanks for the suggestion @PhyoArkarLwinOrdinarily
D
284

os.walk

Use os.walk with next item function:

next(os.walk('.'))[1]

For Python <=2.5 use:

os.walk('.').next()[1]

How this works

os.walk is a generator and calling next will get the first result in the form of a 3-tuple (dirpath, dirnames, filenames). Thus the [1] index returns only the dirnames from that tuple.

Dilution answered 26/9, 2008 at 23:57 Comment(6)
A little more description on this is that this is a generator, it won't be walking the other dirs unless you tell it to. So .next()[1] does in one line what all the list comprehensions do. I'd probably do something like DIRNAMES=1 and then next()[DIRNAMES] to make it easier to understand for future code maintainers.Coppola
+1 amazing solution. To specify a directory to browse, use: os.walk( os.path.join(mypath,'.')).next()[1]Gullett
for python v3: next(os.walk('.'))[1]Castilian
if your going to do more then text processing; ie processing in the actual folders then full paths might be needed: sorted( [os.path.join(os.getcwd(), item) for item in os.walk(os.curdir).next()[1]] )Arjuna
why the dot? next(os.walk('.'))[1]Rifkin
. indicates the current directory. It's equivalent to os.getcwd().Dilution
U
173

Filter the result using os.path.isdir() (and use os.path.join() to get the real path):

>>> [ name for name in os.listdir(thedir) if os.path.isdir(os.path.join(thedir, name)) ]
['ctypes', 'distutils', 'encodings', 'lib-tk', 'config', 'idlelib', 'xml', 'bsddb', 'hotshot', 'logging', 'doc', 'test', 'compiler', 'curses', 'site-packages', 'email', 'sqlite3', 'lib-dynload', 'wsgiref', 'plat-linux2', 'plat-mac']
Undesigning answered 26/9, 2008 at 19:6 Comment(2)
This take a lot of processing vs very simple os.walk().next()[1]Neurophysiology
Doge, to easy! Had to use next(os.walk(dpath))[1] but same concept. Thanks for the suggestion @PhyoArkarLwinOrdinarily
M
74

Filter the list using os.path.isdir to detect directories.

filter(os.path.isdir, os.listdir(os.getcwd()))
Markhor answered 26/9, 2008 at 19:10 Comment(4)
I think this is by far the best combination of readability and conciseness in any of these answers.Dozen
This didn't work. My guess is that os.listdir returns a file/folder name, passed on to os.path.isdir, but the latter needs a full path.Gullett
filter is faster than os.walk timeit(os.walk(os.getcwd()).next()[1]) 1000 loops, best of 3: 734 µs per loop timeit(filter(os.path.isdir, os.listdir(os.getcwd()))) 1000 loops, best of 3: 477 µs per loopFrias
A comment here is that os.listdir does not return the absolute path so filter(os.path.isdir, [os.path.join(base, f) for f in os.listdir(base)]) solved it for me.Gujarati
G
23
directories=[d for d in os.listdir(os.getcwd()) if os.path.isdir(d)]
Gonyea answered 26/9, 2008 at 19:4 Comment(2)
This can be shortened to filter (os.path.isdir, os.listdir (os.getcwd ())Inelegancy
Does anyone have any information on whether filter or a list comprehension is faster? Otherwise its just a subjective argument. This of course assumes there's 10 million directories in the cwd and performance is an issue.Gonyea
D
20

This seems to work too (at least on linux):

import glob, os
glob.glob('*' + os.path.sep)
Darvon answered 6/7, 2016 at 4:54 Comment(3)
+1 for glob. It can save you a lot of code, especially iterations, and is very much akin to UNIX terminal usage (ls)Ives
Rather than glob.glob('*' + os.path.sep) you might want to write [dir for dir in glob.glob("*") if os.path.isdir( dir)]Mcgaw
Note that the strings glob("./*"), glob("*"), glob("*/"), or glob("*" + os.path.sep) returns can be different, such as ".\\" or "\\" can be attached to head or tail of the directory name, in Windows. glob("*") returns exactly the directory name, without ".\\" or "\\".Cahan
T
20

Using list comprehension,

[a for a in os.listdir() if os.path.isdir(a)]

I think It is the simplest way

Tarr answered 10/4, 2019 at 5:29 Comment(1)
to list all subdirs in currentdir: [a for a in os.listdir(currentdir) if os.path.isdir(os.path.join(currentdir, a))]Shipway
D
16

Note that, instead of doing os.listdir(os.getcwd()), it's preferable to do os.listdir(os.path.curdir). One less function call, and it's as portable.

So, to complete the answer, to get a list of directories in a folder:

def listdirs(folder):
    return [d for d in os.listdir(folder) if os.path.isdir(os.path.join(folder, d))]

If you prefer full pathnames, then use this function:

def listdirs(folder):
    return [
        d for d in (os.path.join(folder, d1) for d1 in os.listdir(folder))
        if os.path.isdir(d)
    ]
Duvall answered 26/9, 2008 at 22:32 Comment(0)
L
9

Just to add that using os.listdir() does not "take a lot of processing vs very simple os.walk().next()[1]". This is because os.walk() uses os.listdir() internally. In fact if you test them together:

>>>> import timeit
>>>> timeit.timeit("os.walk('.').next()[1]", "import os", number=10000)
1.1215229034423828
>>>> timeit.timeit("[ name for name in os.listdir('.') if os.path.isdir(os.path.join('.', name)) ]", "import os", number=10000)
1.0592019557952881

The filtering of os.listdir() is very slightly faster.

Loram answered 17/1, 2013 at 11:57 Comment(2)
Coming in Python 3.5 is a faster way of getting directory contents: python.org/dev/peps/pep-0471Loram
pep-0471 - the scandir package - is happily available for Python 2.6 onwards as an installable package on PyPI. It offers replacements for os.walk and os.listdir that are much faster.Loram
A
6

A very much simpler and elegant way is to use this:

 import os
 dir_list = os.walk('.').next()[1]
 print dir_list

Run this script in the same folder for which you want folder names.It will give you exactly the immediate folders name only(that too without the full path of the folders).

Axon answered 13/10, 2014 at 11:26 Comment(0)
T
5

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('./')
[f for f in p.iterdir() if f.is_dir()]
Thermit answered 20/7, 2020 at 18:17 Comment(0)
D
5

You could also use os.scandir:

with os.scandir(os.getcwd()) as mydir:
    dirs = [i.name for i in mydir if i.is_dir()]

In case you want the full path you can use i.path.

Using scandir() instead of listdir() can significantly increase the performance of code that also needs file type or file attribute information, because os.DirEntry objects expose this information if the operating system provides it when scanning a directory.

Dallis answered 12/2, 2021 at 10:48 Comment(0)
M
4
[x for x in os.listdir(somedir) if os.path.isdir(os.path.join(somedir, x))]
Merete answered 26/9, 2008 at 19:5 Comment(0)
S
3

2021 answer using glob:

import glob, os

p = "/some/path/"
for d in glob.glob(p + "*" + os.path.sep):
  print(d)
Skedaddle answered 5/9, 2021 at 23:7 Comment(0)
A
2

being a newbie here i can't yet directly comment but here is a small correction i'd like to add to the following part of ΤΖΩΤΖΙΟΥ's answer :

If you prefer full pathnames, then use this function:

def listdirs(folder):  
  return [
    d for d in (os.path.join(folder, d1) for d1 in os.listdir(folder))
    if os.path.isdir(d)
]

for those still on python < 2.4: the inner construct needs to be a list instead of a tuple and therefore should read like this:

def listdirs(folder):  
  return [
    d for d in [os.path.join(folder, d1) for d1 in os.listdir(folder)]
    if os.path.isdir(d)
  ]

otherwise one gets a syntax error.

Arbitrate answered 27/1, 2011 at 18:25 Comment(2)
i know its been a while, but this first example really helped me.Exscind
You get a syntax error because your version doesn't support generator expressions. These were introduced in Python 2.4 whereas list comprehensions have been available since Python 2.0.Phyte
D
2

FWIW, the os.walk approach is almost 10x faster than the list comprehension and filter approaches:

In [30]: %timeit [d for d in os.listdir(os.getcwd()) if os.path.isdir(d)]
1.23 ms ± 97.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [31]: %timeit list(filter(os.path.isdir, os.listdir(os.getcwd())))
1.13 ms ± 13.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [32]: %timeit next(os.walk(os.getcwd()))[1]
132 µs ± 9.34 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Day answered 3/9, 2020 at 2:24 Comment(2)
What python were you on?Vilayet
Probably Python 3.6Day
B
1

Like so?

>>>> [path for path in os.listdir(os.getcwd()) if os.path.isdir(path)]
Brasilin answered 26/9, 2008 at 19:5 Comment(0)
F
1

For a list of full path names I prefer this version to the other solutions here:

def listdirs(dir):
    return [os.path.join(os.path.join(dir, x)) for x in os.listdir(dir) 
        if os.path.isdir(os.path.join(dir, x))]
Fissiparous answered 20/3, 2013 at 10:53 Comment(0)
P
1
scanDir = "abc"
directories = [d for d in os.listdir(scanDir) if os.path.isdir(os.path.join(os.path.abspath(scanDir), d))]
Pretend answered 18/9, 2017 at 16:4 Comment(0)
P
1

Using python 3.x with pathlib.Path.iter_dir

$ mkdir tmpdir
$ mkdir -p tmpdir/a/b/c
$ mkdir -p tmpdir/x/y/z

$ touch tmpdir/a/b/c/abc.txt
$ touch tmpdir/a/b/ab.txt
$ touch tmpdir/a/a.txt

$ python --version
Python 3.7.12
>>> from pathlib import Path
>>> tmpdir = Path("./tmpdir")
>>> [d for d in tmpdir.iterdir() if d.is_dir]
[PosixPath('tmpdir/x'), PosixPath('tmpdir/a')]
>>> sorted(d for d in tmpdir.iterdir() if d.is_dir)
[PosixPath('tmpdir/a'), PosixPath('tmpdir/x')]
Peripeteia answered 24/3, 2022 at 2:25 Comment(0)
G
0

A safer option that does not fail when there is no directory.

def listdirs(folder):
    if os.path.exists(folder):
         return [d for d in os.listdir(folder) if os.path.isdir(os.path.join(folder, d))]
    else:
         return []
Gnostic answered 21/7, 2017 at 8:1 Comment(0)
A
-1
-- This will exclude files and traverse through 1 level of sub folders in the root

def list_files(dir):
    List = []
    filterstr = ' '
    for root, dirs, files in os.walk(dir, topdown = True):
        #r.append(root)
        if (root == dir):
            pass
        elif filterstr in root:
            #filterstr = ' '
            pass
        else:
            filterstr = root
            #print(root)
            for name in files:
                print(root)
                print(dirs)
                List.append(os.path.join(root,name))
            #print(os.path.join(root,name),"\n")
                print(List,"\n")

    return List
Archaeology answered 26/2, 2018 at 23:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.