Pythonで直接のサブディレクトリをすべて取得する方法


150

すべてのサブディレクトリ(いくつかの例外を除く)でindex.tplをindex.htmlにコピーする簡単なPythonスクリプトを記述しようとしています。

サブディレクトリのリストを取得しようとすると、行き詰まってしまいます。


11
この以前のSOの質問で受け入れられた回答が問題を解決することがわかります。stackoverflow.com
listing

回答:


31

私はいくつかやったテストのスピード返すために、さまざまな機能に完全なパスを現在のすべてのサブディレクトリにします。

tl; dr: 常に使用scandir

list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]

おまけ:では、の代わりにscandirを使用して、単にフォルダ名のみを取得することもできます。f.namef.path

これ(および以下の他のすべての関数)では、自然な並べ替えは使用されません。これは、結果が次のようにソートされることを意味します:1、10、2。自然なソート(1、2、10)を取得するには、https://stackoverflow.com/a/48030307/2441026をご覧ください。




結果scandir次のとおりです。速くより3倍walk速くよりも、32X listdir(フィルター付き)、35倍よりも速くPathlibおよび36Xより速いlistdirと37Xよりも早く(!) glob

Scandir:           0.977
Walk:              3.011
Listdir (filter): 31.288
Pathlib:          34.075
Listdir:          35.501
Glob:             36.277

W7x64、Python 3.8.1でテスト済み。サブフォルダが440個あるフォルダ。os.path.join()を2回実行しないことでスピードアップできる
かどうか疑問に思っている場合listdirは、そうですが、違いは基本的に存在しません。

コード:

import os
import pathlib
import timeit
import glob

path = r"<example_path>"



def a():
    list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
    # print(len(list_subfolders_with_paths))


def b():
    list_subfolders_with_paths = [os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
    # print(len(list_subfolders_with_paths))


def c():
    list_subfolders_with_paths = []
    for root, dirs, files in os.walk(path):
        for dir in dirs:
            list_subfolders_with_paths.append( os.path.join(root, dir) )
        break
    # print(len(list_subfolders_with_paths))


def d():
    list_subfolders_with_paths = glob.glob(path + '/*/')
    # print(len(list_subfolders_with_paths))


def e():
    list_subfolders_with_paths = list(filter(os.path.isdir, [os.path.join(path, f) for f in os.listdir(path)]))
    # print(len(list(list_subfolders_with_paths)))


def f():
    p = pathlib.Path(path)
    list_subfolders_with_paths = [x for x in p.iterdir() if x.is_dir()]
    # print(len(list_subfolders_with_paths))



print(f"Scandir:          {timeit.timeit(a, number=1000):.3f}")
print(f"Listdir:          {timeit.timeit(b, number=1000):.3f}")
print(f"Walk:             {timeit.timeit(c, number=1000):.3f}")
print(f"Glob:             {timeit.timeit(d, number=1000):.3f}")
print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")
print(f"Pathlib:          {timeit.timeit(f, number=1000):.3f}")

1
本当にありがとうございました、これを本当に探していました。素晴らしい分析。
Cing


76

なぜ誰も言及しなかったのglobですか?globUnixスタイルのパス名展開を使用できるようにします。これは、複数のパス名を見つける必要があるほとんどすべての機能に使用できます。それはそれを非常に簡単にします:

from glob import glob
paths = glob('*/')

globほとんどのpathベースのソリューションは最後のスラッシュを省略しますが、UNIXがそうであるように、最後のスラッシュのあるディレクトリを返すことに注意してください。


3
良い解決策、シンプルで機能します。最後のスラッシュが不要な場合は、これを使用できますpaths = [ p.replace('/', '') for p in glob('*/') ]
Evan Hu

5
[p[:-1] for p in paths]replaceメソッドはファイル名のエスケープされたフォワードスラッシュも置換するため、単に最後の文字をでカットした方が安全な場合があります(これらは一般的ではありません)。
2015年

3
さらに安全に、strip( '/')を使用して末尾のスラッシュを削除します。このようにすると、スラッシュ以外の文字を切り取らないことが保証されます
Eliezer Miron

8
構造上、末尾にスラッシュが付いていることが保証されています(そのため、安全ではありません)が、より読みやすいと思います。ただし、後者は完全修飾パスを相対パスに変換するため、のrstrip代わりに使用することをお勧めしstripます。
2016

7
IなどのPython初心者向けの@ariコメントを補完する:strip('/')開始と末尾の両方の「/」rstrip('/')を削除し、末尾の1つだけを削除します
Titou

35

現在のディレクトリにあるすべてのサブディレクトリのリストを取得する」にチェックを入れます。

Python 3のバージョンは次のとおりです。

import os

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

print(dir_list)

2
非常に賢い。効率は重要ではありませんが(...完全に重要です)、これとglobベースのジェネレータ式のどちら(s.rstrip("/") for s in glob(parent_dir+"*/"))が時間効率が高いかについて知りたいです。私の直感的な疑いは、stat()ベースのos.walk()ソリューションシェルスタイルのグロビングよりもはるかに高速であるべきだということです。悲しいことに、私には意志が欠けてtimeitおり、実際にそれを見つけることができません。
セシルカレー

3
これにより、親ディレクトリ名の前にサブディレクトリ名が返されないことに注意してください。
Paul Chernoch 2017

19
import os, os.path

ディレクトリ内の(フルパス)直接サブディレクトリを取得するには:

def SubDirPath (d):
    return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])

最新の(最新の)サブディレクトリを取得するには:

def LatestDirectory (d):
    return max(SubDirPath(d), key=os.path.getmtime)

リストを取得するには、単に追加しlist( filter(...) )ます。
user136036

12

os.walk この状況であなたの友達です。

ドキュメントから直接:

walk()は、ツリーをトップダウンまたはボトムアップでウォークすることにより、ディレクトリツリー内にファイル名を生成します。ディレクトリtop(ルート自体を含む)をルートとするツリー内の各ディレクトリについて、3つのタプル(dirpath、dirnames、filenames)が生成されます。


1
最初のレベルのサブディレクトリだけが必要な場合は、戻り値の最初のセットの後でos.walkの反復から抜け出すことに注意してください。
ヨーヨー、2015年

11

このメソッドは、すべてを一度にうまく実行します。

from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]

7

TwistedのFilePathモジュールを使用する:

from twisted.python.filepath import FilePath

def subdirs(pathObj):
    for subpath in pathObj.walk():
        if subpath.isdir():
            yield subpath

if __name__ == '__main__':
    for subdir in subdirs(FilePath(".")):
        print "Subdirectory:", subdir

一部のコメンターは、これにTwistedのライブラリを使用する利点は何かと尋ねてきたので、ここでは元の質問を少し超えて説明します。


ブランチには、FilePathの利点を説明するいくつかの改善されたドキュメントがあります。あなたはそれを読みたいかもしれません。

より具体的には、この例では、標準ライブラリバージョンとは異なり、この関数はインポートなしで実装できます。「subdirs」関数は完全に汎用的であり、その引数以外には何も作用しません。標準ライブラリを使用してファイルをコピーおよび移動するには、open「組み込みlistdir」、「おそらくisdir」または「os.walk」または「shutil.copy」に依存する必要があります。たぶん「os.path.join」も。実際のファイルを識別するには、文字列に引数を渡す必要があることは言うまでもありません。各ディレクトリの「index.tpl」を「index.html」にコピーする完全な実装を見てみましょう。

def copyTemplates(topdir):
    for subdir in subdirs(topdir):
        tpl = subdir.child("index.tpl")
        if tpl.exists():
            tpl.copyTo(subdir.child("index.html"))

上記の「subdirs」関数は、任意のFilePathようなオブジェクトで機能します。これは、とりわけZipPathオブジェクトを意味します。残念ながらZipPath現時点では読み取り専用ですが、書き込みをサポートするように拡張することもできます。

テスト目的で独自のオブジェクトを渡すこともできます。ここで提案されているos.path-using APIをテストするには、インポートされた名前と暗黙的な依存関係を使用してモンキーを行い、通常、ブラックマジックを実行してテストを機能させる必要があります。FilePathでは、次のようにします。

class MyFakePath:
    def child(self, name):
        "Return an appropriate child object"

    def walk(self):
        "Return an iterable of MyFakePath objects"

    def exists(self):
        "Return true or false, as appropriate to the test"

    def isdir(self):
        "Return true or false, as appropriate to the test"
...
subdirs(MyFakePath(...))

Twistedに触れる機会はほとんどないので、追加の情報や例はいつでも歓迎します。この答えはそのために見るのはうれしいです。とは言っても、このアプローチは組み込みのpythonモジュールを使用するよりもかなり多くの作業とツイストインストールを必要とするように見えるので、これを使用することで回答に追加できる利点はありますか?
ジャレットハーディー

1
Glyphの答えは、TwistedLoreも.tplファイルを使用するという事実に触発されたものと思われます。
コンスタンティン

まあ、明らかに私はスペインの異端審問を期待していません:-)私は "* .tpl"が特定のTwistedテンプレートではなく "テンプレート"を意味するいくつかの抽象的な拡張子への一般的な参照であると仮定しました(多くの.tplが結局のところ、言語)。知ってよかった。
ジャレットハーディー

したがって、可能なツイスト角度に小枝を引くための+1ですが、ツイストされた「FilePath」オブジェクトと「walk()」関数が標準APIに追加するものを理解したいと思います。
ジャレットハーディー

個人的には、「FilePath.walk()はパスオブジェクトを生成する」の方が「os.walkは3タプルのdir、dirs、filesを生成する」よりもはるかに覚えやすいと思います。しかし、他にもメリットがあります。FilePathはポリモーフィズムを可能にします。つまり、ファイルシステム以外のものをトラバースできます。たとえば、twisted.python.zippath.ZipArchiveを 'subdirs'関数に渡して、FilePathsの代わりにZipPathsのジェネレーターを取得できます。ロジックは変わりませんが、アプリケーションはzipファイルを魔法のように処理します。テストしたい場合は、オブジェクトを指定するだけで、実際のファイルを作成する必要はありません。
グリフ

4

私は、VMware仮想マシンを移動するためのコードをいくつか書いただけで、サブディレクトリ間でのファイルのコピーを使用os.pathshutilて実行しました。

def copy_client_files (file_src, file_dst):
    for file in os.listdir(file_src):
            print "Copying file: %s" % file
            shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))

それはひどくエレガントではありませんが、動作します。


1

これが1つの方法です。

import os
import shutil

def copy_over(path, from_name, to_name):
  for path, dirname, fnames in os.walk(path):
    for fname in fnames:
      if fname == from_name:
        shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))


copy_over('.', 'index.tpl', 'index.html')

-1:shutil.copyは現在のディレクトリにコピーするため、機能しません。つまり、サブディレクトリツリーで見つかった「index.tpl」ごとに、現在のディレクトリの「index.html」を1回上書きすることになります。
nosklo 2009

1

私は頻繁に使用するpath.pyライブラリについて言及する必要があります。

直接のサブディレクトリの取得は、次のように簡単になります。

my_dir.dirs()

完全に機能する例は次のとおりです。

from path import Path

my_directory = Path("path/to/my/directory")

subdirs = my_directory.dirs()

注意:Pathは文字列のサブクラスであるため、my_directoryは文字列として操作できますが、パスを操作するための便利なメソッドが多数提供されます


1
def get_folders_in_directories_recursively(directory, index=0):
    folder_list = list()
    parent_directory = directory

    for path, subdirs, _ in os.walk(directory):
        if not index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)
        elif path[len(parent_directory):].count('/') + 1 == index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)

    return folder_list

次の関数は次のように呼び出すことができます。

get_folders_in_directories_recursively(directory、index = 1)->第1レベルのフォルダのリストを提供します

get_folders_in_directories_recursively(directory)->すべてのサブフォルダを提供します


バージョンpython 3.6を実行していますが、関数変数の内部から「自己」を消去する必要がありました
locometro

1
クラス内で使用していた、更新済み
カニッシュマシュー

0
import glob
import os

def child_dirs(path):
     cd = os.getcwd()        # save the current working directory
     os.chdir(path)          # change directory 
     dirs = glob.glob("*/")  # get all the subdirectories
     os.chdir(cd)            # change directory to the script original location
     return dirs

このchild_dirs関数はディレクトリのパスを取得し、その直下のサブディレクトリのリストを返します。

dir
 |
  -- dir_1
  -- dir_2

child_dirs('dir') -> ['dir_1', 'dir_2']

0
import pathlib


def list_dir(dir):
    path = pathlib.Path(dir)
    dir = []
    try:
        for item in path.iterdir():
            if item.is_dir():
                dir.append(item)
        return dir
    except FileNotFoundError:
        print('Invalid directory')

0

pathlibを使用する1つのライナー:

list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.