199

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.

fuentesjr
  • 50,920
  • 27
  • 77
  • 81

21 Answers21

277

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.

Cas
  • 6,123
  • 3
  • 36
  • 35
Alex Coventry
  • 68,681
  • 4
  • 36
  • 40
  • 16
    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. – boatcoder Nov 15 '12 at 15:49
  • 3
    +1 amazing solution. To specify a directory to browse, use: `os.walk( os.path.join(mypath,'.')).next()[1]` – Daniel Reis Dec 04 '12 at 11:53
  • 45
    for python v3: next(os.walk('.'))[1] – Andre Soares Dec 20 '12 at 22:14
  • 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]] )` – DevPlayer Jan 03 '13 at 23:35
  • why the dot? `next(os.walk('.'))[1]` – BERA Dec 21 '20 at 17:43
  • 1
    `.` indicates the current directory. It's equivalent to `os.getcwd()`. – Alex Coventry Dec 22 '20 at 19:29
172

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']
Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
73

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

filter(os.path.isdir, os.listdir(os.getcwd()))
vergenzt
  • 9,669
  • 4
  • 40
  • 47
Colin Jensen
  • 3,739
  • 1
  • 20
  • 17
  • 7
    I think this is by far the best combination of readability and conciseness in any of these answers. – vergenzt Aug 29 '12 at 13:56
  • 34
    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. – Daniel Reis Dec 04 '12 at 11:50
  • 3
    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 loop` – B.Kocis May 18 '16 at 14:19
  • 3
    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. – Hunaphu Oct 22 '20 at 11:41
23
directories=[d for d in os.listdir(os.getcwd()) if os.path.isdir(d)]
Amal Murali
  • 75,622
  • 18
  • 128
  • 150
Mark Roddy
  • 27,122
  • 19
  • 67
  • 71
  • 5
    This can be shortened to filter (os.path.isdir, os.listdir (os.getcwd ()) – John Millikin Sep 26 '08 at 20:20
  • 4
    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. – Mark Roddy Sep 26 '08 at 23:12
20

Using list comprehension,

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

I think It is the simplest way

Pavel Smirnov
  • 4,611
  • 3
  • 18
  • 28
KBLee
  • 271
  • 3
  • 3
  • 1
    to list all subdirs in currentdir: `[a for a in os.listdir(currentdir) if os.path.isdir(os.path.join(currentdir, a))]` – elbe Nov 01 '21 at 11:02
19

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

import glob, os
glob.glob('*' + os.path.sep)
Travis
  • 552
  • 1
  • 8
  • 14
  • 3
    +1 for `glob`. It can save you a lot of code, especially iterations, and is very much akin to UNIX terminal usage (`ls`) – Gerard Aug 09 '16 at 09:12
  • 9
    Rather than glob.glob('\*' + os.path.sep) you might want to write [dir for dir in glob.glob("\*") if os.path.isdir( dir)] – Eamonn M.R. Dec 09 '16 at 21:45
  • 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 "\\". – starriet Aug 26 '23 at 23:47
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)
    ]
tzot
  • 92,761
  • 29
  • 141
  • 204
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.

foz
  • 3,121
  • 1
  • 27
  • 21
  • 2
    Coming in Python 3.5 is a faster way of getting directory contents: https://www.python.org/dev/peps/pep-0471/ – foz Aug 13 '15 at 16:03
  • 1
    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. – foz Feb 03 '17 at 17:06
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).

manty
  • 287
  • 5
  • 19
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()]
joelostblom
  • 43,590
  • 17
  • 150
  • 159
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.

G M
  • 20,759
  • 10
  • 81
  • 84
4
[x for x in os.listdir(somedir) if os.path.isdir(os.path.join(somedir, x))]
Moe
  • 28,607
  • 10
  • 51
  • 67
3

2021 answer using glob:

import glob, os

p = "/some/path/"
for d in glob.glob(p + "*" + os.path.sep):
  print(d)
Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268
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.

Community
  • 1
  • 1
antiplex
  • 938
  • 2
  • 12
  • 17
  • i know its been a while, but this first example really helped me. – Inbar Rose Aug 02 '12 at 09:24
  • 1
    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. – awatts Aug 21 '13 at 10:18
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)
pandichef
  • 706
  • 9
  • 11
1

Like so?

>>>> [path for path in os.listdir(os.getcwd()) if os.path.isdir(path)]
Jens
  • 8,423
  • 9
  • 58
  • 78
Kirk Strauser
  • 30,189
  • 5
  • 49
  • 65
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))]
Community
  • 1
  • 1
1
scanDir = "abc"
directories = [d for d in os.listdir(scanDir) if os.path.isdir(os.path.join(os.path.abspath(scanDir), d))]
nvd
  • 2,995
  • 28
  • 16
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')]
Darren Weber
  • 1,537
  • 19
  • 20
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 []
-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