web-dev-qa-db-ja.com

python os.walkを特定のレベルに

いくつかの基本的なコードを使用してフォルダーを読み取り、フォルダーにあるファイルの数を通知するプログラムを作成したいと思います。これが私が現在それをしている方法です:

import os

folders = ['Y:\\path1', 'Y:\\path2', 'Y:\\path3']
for stuff in folders:
    for root, dirs, files in os.walk(stuff, topdown=True):
        print("there are", len(files), "files in", root)

これは、「メイン」フォルダー内に複数のフォルダーができるまではうまく機能します。これは、フォルダー/ファイル管理が不十分なために、ファイルの長いジャンクリストが返される可能性があるためです。ですから、せいぜい二級だけに行きたいです。例:

Main Folder
---file_i_want
---file_i_want
---Sub_Folder
------file_i_want <--*
------file_i want <--*
------Sub_Folder_2
---------file_i_dont_want
---------file_i_dont_want

breakおよびdel dirs[:]この投稿 および この投稿 から取得して、最初のレベルのみに移動する方法を知っています。

import os
import pandas as pd

folders = ['Y:\\path1', 'Y:\\path2', 'Y:\\path3']
for stuff in folders:
    for root, dirs, files in os.walk(stuff, topdown=True):
        print("there are", len(files), "files in", root)
        del dirs[:] # or a break here. does the same thing.

しかし、どのように検索しても、2層の深さを調べる方法はわかりません。私はそれに関する何か他の投稿を理解していないだけかもしれませんか? del dirs[:2]のようなものを考えていましたが、役に立ちませんでした。誰かが私を案内したり、これを達成する方法を説明したりできますか?

12
MattR

あなたはこのようにすることができます:

_depth = 2

# [1] abspath() already acts as normpath() to remove trailing os.sep
#, and we need ensures trailing os.sep not exists to make slicing accurate. 
# [2] abspath() also make /../ and ////, "." get resolved even though os.walk can returns it literally.
# [3] expanduser() expands ~
# [4] expandvars() expands $HOME
stuff = os.path.abspath(os.path.expanduser(os.path.expandvars(stuff)))

for root,dirs,files in os.walk(stuff):
    if root[len(stuff):].count(os.sep) < depth:
        for f in files:
            print(os.path.join(root,f))
_

キーは:if root[len(stuff):].count(os.sep) < depth

これはstuffrootから削除するため、結果はstuffに対して相対的です。ファイルセパレータの数を数えるだけです。

深さはLinuxのfindコマンドのように機能します。つまり、_-maxdepth 0_は何もしないことを意味し、_-maxdepth 1_は最初のレベルのファイルのみをスキャンし、サブディレクトリに含まれる_-maxdepth 2_ファイルをスキャンします。

もちろん、それでも完全なファイル構造がスキャンされますが、非常に深くない場合は機能します。

別の解決策は、最大の再帰レベルで_os.listdir_を再帰的に(ディレクトリチェック付きで)のみ使用することですが、必要がない場合は少しトリッキーです。それほど難しくないので、ここに1つの実装を示します。

_def scanrec(root):
    rval = []

    def do_scan(start_dir,output,depth=0):
        for f in os.listdir(start_dir):
            ff = os.path.join(start_dir,f)
            if os.path.isdir(ff):
                if depth<2:
                    do_scan(ff,output,depth+1)
            else:
                output.append(ff)

    do_scan(root,rval,0)
    return rval

print(scanrec(stuff))  # prints the list of files not below 2 deep
_

注:_os.listdir_および_os.path.isfile_は2つのstat呼び出しを実行するため、最適ではありません。 Python 3.5では、_os.scandir_を使用すると、その二重呼び出しを回避できます。

13

セパレータを数えることができ、それが2レベルの深さである場合は、dirsの内容を削除して、walkが深く再帰しないようにします。

import os

MAX_DEPTH = 2
folders = ['Y:\\path1', 'Y:\\path2', 'Y:\\path3']
for stuff in folders:
    for root, dirs, files in os.walk(stuff, topdown=True):
        print("there are", len(files), "files in", root)
        if root.count(os.sep) - stuff.count(os.sep) == MAX_DEPTH - 1:
            del dirs[:]

Pythonドキュメント 動作について次のように述べています。

TopdownがTrueの場合、呼び出し元はdirnamesリストをインプレースで(おそらくdelまたはslice割り当てを使用して)変更でき、walk()はdirnamesに名前が残っているサブディレクトリにのみ再帰します。これを使用して、検索を整理したり、特定の訪問順序を課したり、呼び出し元が作成したディレクトリや、名前を変更したりしてwalk()を再開する前にwalk()に通知したりすることもできます。

foldersにあるセパレータを考慮する必要があることに注意してください。たとえば、y:\path1は、ルートはy:\pathしかし、そこで再帰を停止したくありません。

4
niemmi