コース目次 / 第3章

成果物 — フォルダを片づける道具

散らかったフォルダを拡張子ごとに仕分けるスクリプトを作ります。

第3章 / 全5章目安 約60分この章のゴール: 練習用フォルダの中身が、拡張子ごとのフォルダに仕分けられる
お使いのパソコン
手順が切り替わります
お使いのエディタ
ファイル操作が切り替わります

いよいよ道具を作ります。散らかったフォルダを、拡張子ごとに自動で仕分けるスクリプトです。

今日のゴール

ぐちゃぐちゃのフォルダが、こうなります。

実行前
sample/
  memo.txt
  photo.jpg
  report.pdf
  backup.zip
  ...
実行後
sample/
  書類/  memo.txt  readme.md  report.pdf
  画像/  photo.jpg  icon.png
  データ/ kakei-2026-07.csv
  圧縮/  backup.zip
  その他/ nazo.xyz

先に安全の話をします。ファイルを動かすプログラムは、間違えると取り返しがつきません。だから今日書くスクリプトは、そのまま実行してもファイルを1つも動かしません。「何をするつもりか」を並べるだけです。本当に動かすときだけ、合図の言葉を付けます。
そして練習用のフォルダで必ず先に試します。いきなり本物のダウンロードフォルダには向けません。

1. 練習用のフォルダを用意する

seiri-kit の中に sample フォルダを作り、その中に適当なファイルをいくつか置きます。中身は何でもよいので、拡張子だけ揃えてください。下のファイルをそのまま作れば同じ結果になります。

  • sample/backup.zip
  • sample/icon.png
  • sample/kakei-2026-07.csv
  • sample/memo.txt
  • sample/nazo.xyz
  • sample/photo.jpg
  • sample/readme.md
  • sample/report.pdf

中身は空でも構いません。VS Codeで「新しいファイル」を8回くり返すだけです。下のコマンドで、空のファイルを8個まとめて作れます。この sample フォルダは、あとで安心して壊せる練習場になります。

ターミナル派の方

ターミナル
mkdir sampletouch sample/backup.zip sample/icon.png sample/kakei-2026-07.csv sample/memo.txt sample/nazo.xyz sample/photo.jpg sample/readme.md sample/report.pdf

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

PowerShell
mkdir sampleNew-Item sample/backup.zip, sample/icon.png, sample/kakei-2026-07.csv, sample/memo.txt, sample/nazo.xyz, sample/photo.jpg, sample/readme.md, sample/report.pdf -ItemType File

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

2. ミニ解説: 今日使う道具

書き方 何をするか
from pathlib import Path ファイルやフォルダを扱う道具を持ってくる
Path("sample") 「sampleというフォルダ」を表すもの
.iterdir() その中身を1つずつ取り出す
.suffix .jpg のような拡張子の部分
辞書.get(キー, 既定値) 表から引く。無ければ既定値
shutil.move(元, 先) 実際に動かす

pathlibcsvshutil も、Pythonに最初から入っています。何もインストールしません。これがPythonの強みで、「標準ライブラリだけで実用が完結する」とよく言われる所以です。

3. 書く

seiri.py を作って、そのまま書き写します。少し長いですが、上から順に読めるはずです。

seiri.py
"""フォルダの中身を、拡張子ごとのフォルダに仕分けるスクリプト。

そのまま実行すると「何をするつもりか」を並べるだけで、ファイルは動かさない。
本当に動かすときだけ --honban を付ける。
"""

import shutil
import sys
from pathlib import Path

# 拡張子 -> 入れ先のフォルダ名
FOLDERS = {
    ".jpg": "画像",
    ".jpeg": "画像",
    ".png": "画像",
    ".gif": "画像",
    ".pdf": "書類",
    ".txt": "書類",
    ".md": "書類",
    ".csv": "データ",
    ".xlsx": "データ",
    ".zip": "圧縮",
}

# 片づける対象のフォルダ。まずは練習用の sample で試す。
TARGET = Path("sample")


def folder_for(suffix):
    """拡張子から入れ先のフォルダ名を返す。知らない拡張子は「その他」。"""
    return FOLDERS.get(suffix.lower(), "その他")


def main():
    honban = "--honban" in sys.argv

    if not TARGET.exists():
        print(f"{TARGET} というフォルダが見つかりません")
        return

    # 名前がドットで始まるものはシステムが使うファイル(.DS_Store など)なので触らない
    files = sorted(p for p in TARGET.iterdir() if p.is_file() and not p.name.startswith("."))
    if not files:
        print(f"{TARGET} の中に片づけるファイルがありません")
        return

    for path in files:
        folder = folder_for(path.suffix)
        dest = TARGET / folder / path.name

        # 同じ名前がもう置いてあるときは触らない。上書きすると元に戻せない。
        if dest.exists():
            print(f"スキップ(同名あり): {path.name} -> {folder}/")
            continue

        if honban:
            dest.parent.mkdir(exist_ok=True)
            shutil.move(str(path), str(dest))
            print(f"移動しました: {path.name} -> {folder}/")
        else:
            print(f"移動する予定: {path.name} -> {folder}/")

    if not honban:
        print()
        print("これは下見です。ファイルはまだ1つも動いていません。")
        print("本当に動かすときは --honban を付けて実行してください。")


main()

4. まず下見

Mac をお使いの方

VS Codeのターミナルターミナル
python3 seiri.py

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

Windows をお使いの方

VS Codeのターミナルターミナル
py seiri.py

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

こう出ます。

実行結果
移動する予定: backup.zip -> 圧縮/
移動する予定: icon.png -> 画像/
移動する予定: kakei-2026-07.csv -> データ/
移動する予定: memo.txt -> 書類/
移動する予定: nazo.xyz -> その他/
移動する予定: photo.jpg -> 画像/
移動する予定: readme.md -> 書類/
移動する予定: report.pdf -> 書類/

これは下見です。ファイルはまだ1つも動いていません。
本当に動かすときは --honban を付けて実行してください。

sample フォルダを見てください。何も変わっていません。予定を読み上げただけです。

知らない拡張子(.xyz)が「その他」に行くのを確かめてください。FOLDERS.get(拡張子, “その他”) の「無ければ既定値」が効いています。知らないものが来ても止まらない——道具として大事な性質です。

触らないものが2つあります

写したコードには、わざと「何もしない」分岐が2つ入っています。どちらもファイルを失わないための柵です。

書いた行 何を守っているか
if dest.exists(): ... continue 移動先に同じ名前のファイルがあったら、触らない。そのまま移動すると、前からあったファイルがゴミ箱にも入らずに消えます
not p.name.startswith(".") 名前がドットで始まるファイルは触らない。.DS_Store のようなシステムが使うファイルで、動かすと表示が壊れることがあります

確かめてみましょう。sample/画像/ の中に photo.jpg という名前のファイルを1つ作ってから、もう一度下見を実行してください。

その行だけこう変わる
スキップ(同名あり): photo.jpg -> 画像/

下見の時点で分かるのが大事なところです。予定の一覧を読んだときに気づけます。

5. 本番

予定が正しければ、合図を付けて実行します。

Mac をお使いの方

VS Codeのターミナルターミナル
python3 seiri.py --honban

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

Windows をお使いの方

VS Codeのターミナルターミナル
py seiri.py --honban

行頭の $ は「ここからコマンド」という目印です。$ は打たないでください。

移動しました: に変わり、sample の中にフォルダができています。あなたの最初の道具が動きました。

わざと確かめてみる

もう一度 python3 seiri.py(Windowsは py seiri.py)を打ってください。

実行結果
sample の中に片づけるファイルがありません

全部フォルダに入ったので、片づけるものが無くなりました。2回実行しても壊れない——これも道具として大事な性質です。

ハマったら

本物のダウンロードフォルダに使いたい

TARGET = Path("sample") の行を書き換えれば向けられますが、次の章の「あなたの番」まで待ってください。そこで安全なやり方を書きます。焦って本物に向けると、取り返しがつきません。

ファイルが1つも見つからない

sample フォルダが seiri.py と同じ場所にあるか確認してください。VS Code の左側で、seiri.pysample が同じ高さに並んでいれば正解です。ls を打って、seiri.pysample が両方出てくれば正解です。

ZIPで戻したい

このページの下のZIPは、この章を終えた状態のフォルダです。解凍して、その中で作業を続けられます。自分で書き換えた部分は消えてしまうので、先に自分のファイルを別の場所へ待避してから使ってください。

こうなっていればOK

うまくいかないときは、この章の完成ファイル一式(ZIP)をダウンロードして続きから進めても大丈夫です。

卒業まであと1章です。

この章はまだ完了していません。