ディレクトリトラバーサルとファイル探索
os.walk()、globパターン、種類や日付によるファイルのフィルタリングを学びます。
「ディレクトリトラバーサルとファイル探索」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。
エージェントにディレクトリ走査が必要な理由
エージェントは、条件に一致するファイルを見つける必要があることがよくあります。たとえば、コードベース内のすべてのPythonファイル、データディレクトリ内のすべてのCSVファイル、または最近のログなどです。Pythonには、主にos.walk()、Path.iterdir()、Path.rglob()の3つのツールがあります。どの深さまで検索する必要があるかによって、それぞれに異なる長所があります。
import os
from pathlib import Path
# Count all files in a directory tree
root = Path('/tmp/agent_workspace')
file_count = sum(1 for f in root.rglob('*') if f.is_file())
print(f'Found {file_count} files under {root}')os.walk() — 再帰的なディレクトリ走査
os.walk(root)は、ディレクトリツリー内の各ディレクトリについて、(dirpath, dirnames, filenames)のタプルを返します。これは再帰的な走査を行うPythonの従来の方法で、どのサブディレクトリに進むかを細かく制御できます。
import os
import tempfile
# --- demo setup: build a small project tree so the walk has something to show ---
root = tempfile.mkdtemp(prefix='project_')
os.makedirs(os.path.join(root, 'src'), exist_ok=True)
os.makedirs(os.path.join(root, '.git'), exist_ok=True)
with open(os.path.join(root, 'README.md'), 'w') as f:
f.write('demo')
with open(os.path.join(root, 'src', 'main.py'), 'w') as f:
f.write('print(1)')
for dirpath, dirnames, filenames in os.walk(root):
# Skip hidden directories (like .git)
dirnames[:] = [d for d in dirnames if not d.startswith('.')]
print(f'In directory: {dirpath}')
print(f' Subdirs: {dirnames}')
print(f' Files: {filenames}')
for filename in filenames:
full_path = os.path.join(dirpath, filename)
print(f' File: {full_path}')
Path.iterdir() — 1階層の一覧表示
Path.iterdir()は、ディレクトリの直下にある内容を一覧表示します。サブディレクトリ内を再帰的に探索することはありません。ディレクトリの直下の項目だけが必要で、さらに深い階層へ進みたくない場合に使用してください。
from pathlib import Path
data_dir = Path('data')
data_dir.mkdir(exist_ok=True)
(data_dir / 'a.csv').write_text('x')
(data_dir / 'sub').mkdir(exist_ok=True)
for item in data_dir.iterdir():
if item.is_file():
print(f'FILE: {item.name} ({item.stat().st_size} bytes)')
elif item.is_dir():
print(f'DIR: {item.name}/')
csv_files = [f for f in data_dir.iterdir()
if f.is_file() and f.suffix == '.csv']
print(f'Found {len(csv_files)} CSV files')Path.rglob() — 再帰的なグロブ検索
Path.rglob(pattern)は、グロブパターンに一致するすべてのファイルを再帰的に検索します。ディレクトリツリー内のどこにあっても、特定の種類のファイルをすべて見つけるには最も簡潔な方法です。グロブの**は「任意の数のディレクトリ」を意味します。
from pathlib import Path
project = Path('/tmp/my_project')
# Find all Python files recursively
python_files = list(project.rglob('*.py'))
print(f'Found {len(python_files)} Python files:')
for f in python_files:
print(f' {f.relative_to(project)}')
# Find all JSON files
json_files = list(project.rglob('*.json'))
# Find files matching a name pattern
test_files = list(project.rglob('test_*.py'))
print(f'Found {len(test_files)} test files')glob.glob() — パターンマッチング
glob.glob(pattern, recursive=True)は、従来のグロブ用インターフェースです。recursive=Trueを指定すると、**ワイルドカードが任意のサブディレクトリのパスに一致します。戻り値は文字列ですが、pathlib.rglobはPathオブジェクトを返します。
import glob
# Find all CSV files recursively
csv_files = glob.glob('data/**/*.csv', recursive=True)
print(f'Found {len(csv_files)} CSV files')
for f in csv_files:
print(f' {f}')
# Find all log files in a specific directory (non-recursive)
logs = glob.glob('/var/log/*.log')
# Find files matching multiple criteria
import fnmatch
all_files = glob.glob('/tmp/**/*', recursive=True)
config_files = [
f for f in all_files
if fnmatch.fnmatch(f, '*.yaml') or fnmatch.fnmatch(f, '*.yml')
]
print(f'Found {len(config_files)} YAML config files')ファイル拡張子によるフィルタリング
ディレクトリを走査するときは、Path.suffix(ドットを含みます。たとえば'.py')を使うか、許可する拡張子の集合と照合してファイルを拡張子でフィルタリングします。複数の拡張子を対象にする場合、グロブパターンよりも高速で正確です。
from pathlib import Path
directory = Path('/tmp/mixed_files')
ALLOWED_EXTENSIONS = {'.py', '.js', '.ts', '.json', '.yaml'}
# Filter by allowed extensions
code_files = [
f for f in directory.rglob('*')
if f.is_file() and f.suffix.lower() in ALLOWED_EXTENSIONS
]
print(f'Found {len(code_files)} code/config files')
# Group files by extension
from collections import defaultdict
by_ext = defaultdict(list)
for f in directory.rglob('*'):
if f.is_file():
by_ext[f.suffix].append(f)
for ext, files in sorted(by_ext.items()):
print(f'{ext}: {len(files)} files')ファイルサイズによるフィルタリング
エージェントでは、サイズのしきい値を上回る、または下回るファイルを見つける必要がある場合があります。たとえば、小さく空のファイルを飛ばしたり、メモリを圧迫する巨大なファイルの処理を避けたりします。Path.stat().st_sizeを使用すると、ファイルサイズをバイト単位で取得できます。
from pathlib import Path
data_dir = Path('/tmp/data')
MIN_SIZE = 1024 # 1 KB — skip empty/tiny files
MAX_SIZE = 50_000_000 # 50 MB — skip very large files
processable = []
skipped_small = []
skipped_large = []
for f in data_dir.rglob('*.json'):
if not f.is_file():
continue
size = f.stat().st_size
if size < MIN_SIZE:
skipped_small.append(f.name)
elif size > MAX_SIZE:
skipped_large.append(f.name)
else:
processable.append(f)
print(f'Processable: {len(processable)}')
print(f'Too small: {len(skipped_small)}')
print(f'Too large: {len(skipped_large)}')更新日時によるフィルタリング
増分処理パイプラインのエージェントでは、前回の実行以降に変更されたファイルだけを処理すればよい場合があります。stat().st_mtime(Unixタイムスタンプとしての更新日時)を使用すると、最近更新されたファイルを見つけられます。
from pathlib import Path
import time
import datetime
data_dir = Path('/tmp/data')
# Files modified in the last 24 hours
cutoff = time.time() - (24 * 60 * 60)
recent_files = [
f for f in data_dir.rglob('*.log')
if f.is_file() and f.stat().st_mtime > cutoff
]
print(f'Modified in last 24h: {len(recent_files)} files')
# Sort by modification time (newest first)
recent_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
for f in recent_files[:5]: # top 5 most recent
mtime = datetime.datetime.fromtimestamp(f.stat().st_mtime)
print(f' {f.name}: {mtime.strftime("%Y-%m-%d %H:%M:%S")}')隠しファイルとシステムディレクトリをスキップする
コードリポジトリやユーザーディレクトリを走査するときは、隠しファイル(.で始まるファイル)と、.git、__pycache__、node_modules、.venvなどのシステムディレクトリをスキップしてください。これらには、ほとんどのエージェントにとって不要なファイルが何千個も含まれています。
from pathlib import Path
SKIP_DIRS = {'.git', '__pycache__', 'node_modules', '.venv',
'.env', 'dist', 'build', '.mypy_cache'}
def iter_source_files(root, extensions):
root = Path(root)
for path in root.rglob('*'):
# Skip if any parent is in SKIP_DIRS
if any(part in SKIP_DIRS for part in path.parts):
continue
# Skip hidden files
if path.name.startswith('.'):
continue
if path.is_file() and path.suffix in extensions:
yield path
python_files = list(iter_source_files('/tmp/project', {'.py'}))
print(f'Found {len(python_files)} Python source files')ファイルインベントリの作成
ファイルインベントリとは、ディレクトリツリーを構造化してまとめたものです。処理の前に作業を計画する必要があるエージェントに役立ちます。名前、パス、サイズ、拡張子、更新日時を収集し、JSONにシリアライズできる辞書のリストにまとめます。
from pathlib import Path
import datetime
import json
def build_inventory(root_dir, pattern='**/*'):
root = Path(root_dir)
inventory = []
for f in root.glob(pattern):
if not f.is_file():
continue
stat = f.stat()
inventory.append({
'name': f.name,
'path': str(f.relative_to(root)),
'extension': f.suffix,
'size_bytes': stat.st_size,
'modified': datetime.datetime.fromtimestamp(
stat.st_mtime
).isoformat()
})
return sorted(inventory, key=lambda x: x['path'])
inventory = build_inventory('/tmp/data')
with open('file_inventory.json', 'w') as out:
json.dump(inventory, out, indent=2)
print(f'Inventoried {len(inventory)} files')ディレクトリの変更を監視する
ディレクトリに新しいファイルが現れたときに反応する必要があるエージェントもあります。本番環境ではwatchdogのような本格的なファイル監視ライブラリが最適ですが、簡単な方法として、ディレクトリを定期的にスキャンし、既知のファイル集合と比較することもできます。
from pathlib import Path
import time
def watch_for_new_files(watch_dir, extension='.json', interval=5):
watch_dir = Path(watch_dir)
known_files = set(watch_dir.glob(f'*{extension}'))
print(f'Watching {watch_dir} for new {extension} files...')
while True:
time.sleep(interval)
current_files = set(watch_dir.glob(f'*{extension}'))
new_files = current_files - known_files
for f in new_files:
print(f'New file detected: {f.name}')
process_new_file(f) # handle the new file
known_files = current_files
def process_new_file(file_path):
print(f'Processing: {file_path}')
# --- demo (one polling cycle, without the infinite while True loop) ---
import tempfile
demo_dir = Path(tempfile.mkdtemp())
known_files = set(demo_dir.glob('*.json'))
print(f'Watching {demo_dir} for new .json files...')
(demo_dir / 'result_a.json').write_text('{}', encoding='utf-8')
(demo_dir / 'result_b.json').write_text('{}', encoding='utf-8')
current_files = set(demo_dir.glob('*.json'))
new_files = current_files - known_files
for f in sorted(new_files, key=lambda p: p.name):
print(f'New file detected: {f.name}')
process_new_file(f)
クイックチェック:rglobとiterdirの比較
ディレクトリ走査の方法についての理解度を確認しましょう。
ディレクトリ走査のまとめ
これで、エージェントに必要なあらゆるファイルを見つけられるようになりました。
- os.walk() — 再帰的な走査を細かく制御でき、
dirnamesをその場で変更してサブディレクトリを探索対象から外せます - Path.iterdir() — 直下の項目だけを1階層で一覧表示します
- Path.rglob('*.ext') — パターンによる再帰検索を簡潔に行えます
- glob.glob('**/*.ext', recursive=True) — 文字列を返す従来の方法です
- 拡張子には
.suffix、サイズには.stat().st_size、更新日時には.stat().st_mtimeでフィルタリングします .gitやnode_modulesなどの隠しファイルとシステムディレクトリは、常にスキップしてください
よくある質問
「ディレクトリトラバーサルとファイル探索」レッスンは無料ですか?
はい。「ディレクトリトラバーサルとファイル探索」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。
「ディレクトリトラバーサルとファイル探索」で何を学びますか?
os.walk()、globパターン、種類や日付によるファイルのフィルタリングを学びます。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
AI Agentsを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「ディレクトリトラバーサルとファイル探索」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAI Agentsレッスンでコードを書いて実行できますか?
はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- エージェントコンテキストでのファイルの読み書き
- ディレクトリトラバーサルとファイル探索
- ファイル形式の処理:CSV、JSON、TXT
- エラーハンドリングを伴う安全なファイル操作