トップ/記事一覧/Ubuntuのスクリーンショットを「セッション単位」で自動フォルダ分けする
ubuntu-screenshot-session-sort-cover

Ubuntuのスクリーンショットを「セッション単位」で自動フォルダ分けする

Ubuntu標準のスクリーンショット機能だけで撮り続けると保存フォルダがすぐに汚れる。追加ソフト不要で、systemdのuser path unitを使い「前の1枚から2時間以上空いたら別の作業」とみなしてセッション単位に自動仕分けする仕組みを作った話。

ラボ2026年7月15日公開 本日更新
目次
この記事のポイント

Ubuntu標準のスクリーンショット機能だけで撮り続けると保存フォルダがすぐに汚れる。追加ソフト不要で、systemdのuser path unitを使い「前の1枚から2時間以上空いたら別の作業」とみなしてセッション単位に自動仕分けする仕組みを作った話。

UbuntuでPrintキーを押してスクリーンショットを撮る機会が多いのですが、気づけば保存フォルダに何百枚もの画像がフラットに並び、「これは何の作業の時のキャプチャだっけ」と探し出せなくなっていました。この記事では、追加のソフトを何も入れずに、標準機能だけでスクリーンショットを自動的にフォルダ分けする仕組みを作った話を書きます。最終的には「日付ごとに分ける」から一歩進んで、「前の1枚から一定時間が空いたら別の作業とみなして新しいフォルダを作る」という、体感に近い分け方にたどり着きました。

自動で日付+時刻のセッションフォルダに仕分けされた ~/ピクチャ/Screenshots のファイルマネージャ画面
実際に自動仕分けされた後の ~/ピクチャ/Screenshots。日付+時刻(セッション)ごとのフォルダにきれいに分かれている

私の環境

今回の作業環境は次の通りです。特別なツールは入れておらず、Ubuntuに最初から入っている機能だけで完結しています。

  • OS: Ubuntu(デスクトップ環境はGNOME)
  • スクリーンショット機能: GNOME標準のPrintキー(範囲選択はShift+Print)。追加インストール不要でOS標準搭載
  • 保存先: ~/Pictures/Screenshots(標準のホームフォルダ名。日本語環境では~/ピクチャ/Screenshots)
  • 使ったのはシェルスクリプトと、OSに標準で入っているsystemdだけ

GNOMEの標準スクリーンショット機能は、ファイル名に撮影した日時をそのまま埋め込んで保存してくれます。例えばScreenshot From 2026-07-15 13-04-51.pngのような名前です。この「ファイル名に日時が入っている」という性質が、後の仕組み作りでそのまま使える手がかりになりました。

今回やりたいことと背景

きっかけは単純で、保存フォルダを開くたびに同じ画像の羅列を延々スクロールして目当ての1枚を探すのに疲れたことです。最初に考えたのは「日付ごとにフォルダを分ければいいのでは」という案でした。実際にこれはすぐ作れて、ファイル名から日付部分だけを取り出し、YYYY-MM-DDという名前のフォルダへ移動するだけです。

何ヶ月分ものスクリーンショットが1つのフォルダにフラットに並んでいるファイルマネージャ画面
作業前はこんな感じで、何か月分ものスクショが1つのフォルダにフラットに並んでいた

ただ、実際に運用してみるとしっくりきませんでした。1日の中でも、朝に調べ物をしていた時のキャプチャと、午後に別の作業をしていた時のキャプチャが同じフォルダに混ざってしまい、結局「その日1日分」を全部見返す必要があったからです。自分の感覚では、「前のスクショから2時間くらい間が空いていたら、それはもう別の作業をしていたはず」という区切りの方が実態に合っていました。そこで、日付ではなく「作業のまとまり(セッション)」でフォルダを分ける方式に作り直すことにしました。しかもこの「2時間」という区切りは自分の感覚に過ぎないので、後から気軽に変えられるようにしておく必要もありました。

systemdとは

systemdは、Ubuntuを含む多くのLinuxディストリビューションに標準で入っている「システムとサービスの管理者」です。パソコンの電源を入れた時に何をどの順番で起動するか、常駐させておきたいプログラムが落ちた時に自動で再起動するか、といった管理を一手に引き受けています。普段は裏方なので意識することは少ないですが、「決まった条件で何かを自動実行する」という土台として、実は個人の作業の自動化にもそのまま流用できます。

systemdが管理する対象は「ユニット」という単位で定義されます。よく使われるのは、プログラムそのものを起動・常駐させるserviceユニットと、一定間隔で処理を実行するtimerユニットです。今回はこれに加えて、「特定のファイルやフォルダに変化があったら発火する」pathユニットを使いました。

また、システム全体に関わる設定はroot権限で/etc/systemd/system/以下に置きますが、今回のような「自分のログイン中だけ動けばいい個人的な自動化」にはsystemd --userという仕組みが使えます。ユニットファイルを~/.config/systemd/user/に置くだけで、sudo権限なしに自分専用のサービスとして登録・管理できます。

user path unitとは

今回の主役であるpathユニットは、指定したファイルやディレクトリを監視して、変化があった瞬間に対応するserviceユニットを起動する仕組みです。「〇分おきに確認する」というポーリング(定期巡回)ではなく、OSのファイルシステムの変更通知の仕組みに直接乗っかっているため、変化があった瞬間にほぼ即座に反応します。しかも設定はテキストファイル2枚を置くだけで完了します。

監視条件にはいくつか種類があり、ファイルが「存在するかどうか」を見るPathExistsや、中身が空でないかを見るDirectoryNotEmptyなどがあります。今回はスクリーンショット保存フォルダそのものの中身が変わった(=新しい画像が追加された)ことを検知したいので、PathModifiedを使いました。

採用しなかった方法(inotify)

「フォルダの変化を検知する」と聞いて真っ先に思い浮かぶのは、Linuxのカーネル機能であるinotifyを直接使う方法です。inotify-toolsというパッケージを入れると、inotifywaitというコマンドで「このフォルダに変化があるまで待つ」という処理をシェルスクリプトの中に直接書けます。監視ループを自分で書いて、変化を検知したら仕分けスクリプトを呼ぶ、という構成も十分作れます。

ただ、この方法は今回の環境にはinotify-toolsパッケージが入っておらず、sudo apt installでパッケージを追加する必要がありました。加えて、監視ループ自体をバックグラウンドで動かし続ける常駐プロセスとして自分で管理しなければならず、ログイン時の自動起動やプロセスが落ちた時の再起動といった面倒も自分で作り込む必要があります。

一方のsystemdのpathユニットは、内部的にはまさにこのinotifyの仕組みを使っています。つまり、追加のパッケージを一切インストールしなくても、OS標準の機能だけで同じことが実現できるということです。しかも常駐・自動起動・再起動の管理はsystemdが引き受けてくれるので、自分で書くのは「仕分け処理そのもの」のスクリプトだけで済みます。この理由から、今回はinotifyを直接使う方法は採用せず、systemdのpath unitを選びました。

実装したコード

全体の構成は次の4つのファイルです。

  • ~/.config/systemd/user/screenshot-sort.path — フォルダを監視する
  • ~/.config/systemd/user/screenshot-sort.service — 変化があった時に仕分けスクリプトを起動する
  • ~/.local/bin/sort-screenshots.sh — 実際の仕分け処理を行う本体
  • ~/.config/screenshot-sort/config — セッションの区切り時間を設定する

監視役: screenshot-sort.path

スクリーンショット保存フォルダを監視し、変化があればscreenshot-sort.serviceを起動するだけの短いユニットです。

[Unit]
Description=Watch Screenshots directory for new files

[Path]
PathModified=%h/ピクチャ/Screenshots
Unit=screenshot-sort.service

[Install]
WantedBy=default.target

%hはsystemdが自動的に自分のホームディレクトリへ置き換えてくれる変数です。日本語ロケール環境なので保存フォルダもピクチャという日本語名になっていますが、そのままパスに書けば問題なく動きます。

実行役: screenshot-sort.service

仕分けスクリプトを1回だけ実行して終わるoneshotタイプのサービスです。撮影直後はまだファイルへの書き込みが完全に終わっていない可能性を考え、1秒だけ待ってから実行するようにしています。

[Unit]
Description=Sort screenshots into date-based subdirectories

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 1
ExecStart=%h/.local/bin/sort-screenshots.sh

本体: sort-screenshots.sh

ここが今回のロジックの中心です。フォルダ直下に落ちているファイルを、ファイル名に埋め込まれた撮影日時(取れない画像はファイルの更新日時で代用)でソートし、直前のファイルとの間隔が設定値を超えていたら新しいセッションフォルダを作って移動します。

「セッションの継続中かどうか」を正しく判断するには、直前に処理したファイルの時刻と、現在使っているセッションフォルダ名を覚えておく必要があります。ただしpathユニットは変化のたびに毎回新しいプロセスとしてスクリプトを起動するため、スクリプト内の変数にただ入れておくだけでは次の実行で消えてしまいます。そこで、この2つの値を小さな状態ファイルに保存し、実行のたびに読み書きすることでセッションの連続性を保っています。

#!/bin/bash
# ~/ピクチャ/Screenshots 直下に落ちたスクショを、前のスクショから GAP_HOURS
# 以上間が空いたかどうかでセッション単位のサブディレクトリに振り分ける。
# systemd path unit から呼ばれる。
set -euo pipefail

SCREENSHOTS_DIR="$HOME/ピクチャ/Screenshots"
CONFIG_FILE="$HOME/.config/screenshot-sort/config"
STATE_FILE="$HOME/.local/state/screenshot-sort/state"

[ -d "$SCREENSHOTS_DIR" ] || exit 0

GAP_HOURS=2
[ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"
GAP_SECONDS=$(( GAP_HOURS * 3600 ))

mkdir -p "$(dirname "$STATE_FILE")"
LAST_EPOCH=0
CURRENT_DIR=""
[ -f "$STATE_FILE" ] && source "$STATE_FILE"

# 「ファイル名から抽出したepoch<TAB>ファイル名」の一覧を時刻順に作る
entries=()
while IFS= read -r -d '' file; do
    name="$(basename "$file")"
    ts="$(grep -oiP '\d{4}-\d{2}-\d{2} \d{2}-\d{2}-\d{2}' <<< "$name" | head -1 || true)"
    if [ -n "$ts" ]; then
        epoch="$(date -d "${ts:0:10} ${ts:11:2}:${ts:14:2}:${ts:17:2}" +%s)"
    else
        epoch="$(stat -c '%Y' "$file")"
    fi
    entries+=("$epoch"$'\t'"$name")
done < <(find "$SCREENSHOTS_DIR" -maxdepth 1 -type f -print0)

[ ${#entries[@]} -eq 0 ] && exit 0

while IFS=$'\t' read -r epoch name; do
    if [ -z "$CURRENT_DIR" ] || [ $(( epoch - LAST_EPOCH )) -gt "$GAP_SECONDS" ]; then
        CURRENT_DIR="$(date -d "@$epoch" '+%Y-%m-%d_%H-%M')"
    fi

    destdir="$SCREENSHOTS_DIR/$CURRENT_DIR"
    mkdir -p "$destdir"

    src="$SCREENSHOTS_DIR/$name"
    dest="$destdir/$name"
    if [ -e "$dest" ]; then
        dest="$destdir/${epoch}_$name"
    fi
    mv -n -- "$src" "$dest"

    LAST_EPOCH="$epoch"
done < <(printf '%s\n' "${entries[@]}" | sort -t $'\t' -k1,1n)

{
    echo "LAST_EPOCH=$LAST_EPOCH"
    echo "CURRENT_DIR=$CURRENT_DIR"
} > "$STATE_FILE"

セッションフォルダの名前は、そのセッション最初のスクショの時刻をそのまま使い、2026-07-15_13-04のような形にしています。同じ日でも間隔が空けば別フォルダになるので、フォルダ名を見ただけで「その日の何時ごろの作業か」がひと目でわかります。

設定: screenshot-sort/config

「2時間」という区切りは自分の感覚で決めた値なので、後から気軽に変えられるよう、スクリプト本体とは別の設定ファイルに切り出しています。

# この時間(単位:時間)以上、前のスクショから間が空いたら別セッションとみなす
GAP_HOURS=2

この仕組みを有効にするコマンドは次の2行だけです。

systemctl --user daemon-reload
systemctl --user enable --now screenshot-sort.path

結果

導入前は261枚のスクリーンショットが1つのフォルダにフラットに溜まっていました。まず日付ごとに仕分けたところ54個のフォルダになり、その後セッション単位で作り直したところ80個のフォルダになりました。1日の中で複数回スクショを撮っていた日は、ちゃんと「午前の作業」「午後の作業」のように別フォルダへ分かれています。ファイル数は移行の前後で261枚のまま変わらないことも確認済みです。

実際に試しのスクショを保存フォルダに置いて動作確認したところ、数秒以内に自動でその時刻のセッションフォルダへ移動されました。設定ファイルもスクリプト本体も一切いじらず、待つだけで仕分けが完了します。これでスクリーンショットの保存フォルダを開いた時に「これはあの時の作業だ」とフォルダ名だけで判断できるようになり、目当ての1枚を延々スクロールして探す必要がなくなりました。

avatar-m-1

堀川 慎

Backend Engineer / AWS / Django / Go