AIと一緒に業務を進めることが増えましたが、セッションが切れたり、環境によっては履歴を残せなかったりして、案件復帰に時間がかかることがあります。
そこで「検索しやすい形」で業務メモを残す運用にしてましたが、さらに強化しようと思います。
シンプルですが、業務メモ管理としてかなり快適になると思います。
やっていること(全体像)
- サクラエディタでメモ作成
- 新規作成時に日付ベースのファイル名を自動生成(既存)
- Markdown風ヘッダで「タイトル・タグ・拡張子」を管理(新規)
- Pythonでフォルダ整理+空ファイル削除+ファイル名リネーム(既存と新規)
- treeコマンドで検索(既存)
新規作成時のファイル名ルール
yyyymmdd_no.ext
フォルダ整理後のファイル名ルール(最終形)
20260211_001[タグ][タグ]_title.txt
例:
20260211_001[調査][AWS移行]_S3権限エラー.txt
20260212_001[SQL][顧客抽出]_顧客抽出.sql
treeコマンドの表示だけで内容の見当がつきます。
メモ本文のフォーマット(自動挿入)
※「きっかけ/状況/仮説/次やる」は、未来の自分が案件復帰するための“思考ログ”です。
タイトル:
タグ: [タグ][タグ]
拡張子: txt、sql、csv、md
以下は思考の保存メモ
きっかけ:
状況:
仮説:
次やる:
========
(本文)
タグは複数指定できます。
よく使う検索キーワードはフォーマットとして入れておくと便利です。不要なら削除すればOKです。
思考ログの例:
思考の保存メモ
きっかけ:顧客からS3エラー報告
状況:IAM権限が怪しいが未確認
仮説:ロール設定ミス?
次やる:CloudWatchログ確認
========
ログURL:
エラー内容:
対応メモ:
サクラエディタのマクロでやっていること
新規作成時に:
1. 日付+連番+拡張子を生成
2. ヘッダ部を挿入
サクラエディタのマクロ
マクロに処理を詰め込みすぎるとうまく動作しなかったため、
最終的なファイル名生成は Python 側に移しました。
結果として、サクラエディタのマクロはシンプルになっています。
例:sakura_memo_autosave.js(動作確認済)
// sakura_memo_autosave_simple.js
// Sakura Editor Script (JScript / WSH)
/*
============================================================
タグ候補メモ(※このコメントは本文には挿入されません)
------------------------------------------------------------
■用途系
[調査] [作業] [障害] [問い合わせ] [仕様] [設計] [レビュー] [運用]
■技術/領域
[SQL] [AWS] [IAM] [S3] [Lambda] [CloudWatch] [DynamoDB]
[API] [Batch] [ETL] [権限] [ログ] [性能] [バグ]
■相手/範囲
[顧客A] [顧客B] [社内] [他部署] [ベンダー] [案件名]
■状態
[未調査] [調査中] [原因特定] [暫定対応] [恒久対応] [要確認] [完了]
============================================================
*/
(function () {
var wsh_shell = new ActiveXObject("WScript.Shell");
var CONFIG = {
savedir: wsh_shell.ExpandEnvironmentStrings("%USERPROFILE%") + "\\Desktop\\memo",
charset: "UTF-8",
linebreak: "CRLF"
// ▼ ヘッダ自動挿入をやめたのでコメントアウト
/*
header_template:
"タイトル: \r\n" +
"タグ: [調査][案件名]\r\n" +
"拡張子:txt csv sql\r\n" +
"\r\n" +
"思考の保存メモ\r\n" +
"きっかけ:\r\n" +
"状況:\r\n" +
"仮説:\r\n" +
"次やる:\r\n" +
"========\r\n" +
"\r\n"
*/
};
// 既に保存済みなら何もしない
if (Editor.GetFilename() != "") return;
// 保存フォルダなければ作成
var fso = new ActiveXObject("Scripting.FileSystemObject");
if (!fso.FolderExists(CONFIG.savedir)) {
fso.CreateFolder(CONFIG.savedir);
}
// ▼ ヘッダ挿入処理を停止
/*
if (isEmptyDocument()) {
Editor.InsText(CONFIG.header_template);
}
*/
// ---- ファイル名生成(日付+連番のみ) ----
var now = getNow();
var number = Editor.ExpandParameter("$n");
var filename = now + "_" + number + ".txt";
// 重複回避(念のため)
if (fso.FileExists(CONFIG.savedir + "\\" + filename)) {
filename = now + "_" + number + "_" + number + ".txt";
}
var path = CONFIG.savedir + "\\" + filename;
Editor.FileSaveAs(path, CONFIG.charset, CONFIG.linebreak);
// ---------- utilities ----------
// ▼ ヘッダ判定に使っていたので不要ならコメントアウトOK
/*
function isEmptyDocument() {
for (var i = 0; i < 50; i++) {
try {
if (Editor.GetLineStr(i).replace(/\s/g, "") !== "") {
return false;
}
} catch (e) {
return true;
}
}
return true;
}
*/
function getNow() {
var d = new Date();
return (
d.getFullYear() +
("0" + (d.getMonth() + 1)).slice(-2) +
("0" + d.getDate()).slice(-2)
);
}
})();
注)環境によってはsjisで保存しないと文字化けします。
保存例
memo/
├ memo_sort.py
├ memo_sort.bat
├ 20260211_001.txt
├ 20260212_001.txt
Python側の整理処理
別スクリプトで:
• 0KBファイル削除(保存だけして未記入のメモ対策)
• 日付フォルダに整理(yyyy/yyyymm/yyyymmdd)
※ ファイル名先頭が yyyymmdd 形式である前提です。
例:memo_sort.py(動作確認済)
import os
import re
import shutil
# =========================
# 設定
# =========================
SRC_DIR = r"C:\Users\あなた\Desktop\memo" # ←変更してください
DEST_ROOT = SRC_DIR
# 初期ファイル名想定:yyyymmdd_no.txt(noは001みたいな連番 or 数字)
# 例: 20260214_001.txt / 20260214_1.txt
FNAME_RE = re.compile(r"^(?P<yyyymmdd>\d{8})_(?P<no>\d+)(?P<rest>.*)$")
# ============================================================
# ▼ ここから下は「タグ/タイトル/拡張子を拾ってリネーム」するための部品
# 今回は “日付フォルダに移動だけ” にしたので使いません。
# (後で復活できるようにコメントで残してあります)
# ============================================================
# # ヘッダ抽出(先頭付近だけ読む)
# TITLE_RE = re.compile(r"^タイトル:\s*(.*)$")
# TAGS_RE = re.compile(r"^タグ:\s*(.*)$")
# EXT_RE = re.compile(r"^拡張子:\s*(.*)$")
#
# # Windowsファイル名禁止文字
# INVALID_CHARS_RE = re.compile(r'[\\\/:*?"<>|]')
#
# ALLOWED_EXT = {"txt", "sql", "csv", "md"}
#
# def read_lines_with_fallback(path, max_lines=50):
# """
# 会社PCだとSJIS/CP932のことが多いので、まずUTF系→ダメならcp932で読む。
# """
# for enc in ("utf-8-sig", "utf-8", "cp932"):
# try:
# with open(path, "r", encoding=enc, errors="strict") as f:
# lines = []
# for _ in range(max_lines):
# line = f.readline()
# if line == "":
# break
# lines.append(line.rstrip("\r\n"))
# return lines
# except Exception:
# continue
#
# # 最後の手段:文字化けしても読む(リネームはできる)
# with open(path, "r", encoding="cp932", errors="replace") as f:
# lines = []
# for _ in range(max_lines):
# line = f.readline()
# if line == "":
# break
# lines.append(line.rstrip("\r\n"))
# return lines
#
# def sanitize_title(title: str) -> str:
# title = title.strip()
# if not title:
# return ""
# title = INVALID_CHARS_RE.sub("", title)
# title = re.sub(r"[\u0000-\u001F]", "", title) # 制御文字
# title = re.sub(r"[\. ]+$", "", title) # 末尾の . や空白はNG
# return title.strip()
#
# def sanitize_tags(raw_tags: str) -> str:
# raw_tags = raw_tags.strip()
# if not raw_tags:
# return ""
# raw_tags = INVALID_CHARS_RE.sub("", raw_tags)
# raw_tags = re.sub(r"[\. ]+$", "", raw_tags)
# return raw_tags.strip()
#
# def sanitize_ext(raw_ext: str, fallback: str) -> str:
# ext = (raw_ext or "").strip().lower().lstrip(".")
# if ext in ALLOWED_EXT:
# return ext
# return fallback
#
# def parse_header_meta(path: str):
# title = ""
# tags = ""
# ext = ""
#
# for line in read_lines_with_fallback(path, max_lines=50):
# s = line.strip()
# if s.startswith("========"):
# break
#
# m = TITLE_RE.match(s)
# if m:
# title = m.group(1).strip()
# continue
#
# m = TAGS_RE.match(s)
# if m:
# tags = m.group(1).strip()
# continue
#
# m = EXT_RE.match(s)
# if m:
# ext = m.group(1).strip()
# continue
#
# return title, tags, ext
#
# def ensure_unique_path(dir_path: str, filename: str) -> str:
# base, ext = os.path.splitext(filename)
# cand = filename
# i = 2
# while os.path.exists(os.path.join(dir_path, cand)):
# cand = f"{base}_{i}{ext}"
# i += 1
# return os.path.join(dir_path, cand)
# =========================
# メイン処理(移動だけ)
# =========================
for filename in os.listdir(SRC_DIR):
path = os.path.join(SRC_DIR, filename)
# ファイルのみ対象
if not os.path.isfile(path):
continue
# 0KBファイル削除(不要ならこのブロックもコメントアウトOK)
if os.path.getsize(path) == 0:
print("削除:", filename)
os.remove(path)
continue
# ファイル名から日付取得(yyyymmdd)
m = FNAME_RE.match(filename)
if not m:
print("日付なし:", filename)
continue
yyyymmdd = m.group("yyyymmdd")
yyyy = yyyymmdd[:4]
yyyymm = yyyymmdd[:6]
# 日付フォルダ作成
dest_dir = os.path.join(DEST_ROOT, yyyy, yyyymm, yyyymmdd)
os.makedirs(dest_dir, exist_ok=True)
# 移動先パス
dest_path = os.path.join(dest_dir, filename)
# すでに移動済みならスキップ
if os.path.abspath(path) == os.path.abspath(dest_path):
continue
# 移動
print("移動:", filename, "→", dest_dir)
shutil.move(path, dest_path)
# ------------------------------------------------------------
# ▼ リネーム処理(タグ/タイトル/拡張子反映)は今回は使わない
# ------------------------------------------------------------
# title, tags, header_ext = parse_header_meta(dest_path)
# ...
# os.rename(dest_path, new_path)
ダブルクリックで実行できるバッチファイル
例:memo_sort.bat(動作確認済)
@echo off
cd /d %~dp0
python memo_sort.py
pause
注)python部分は環境に合わせる(py, py -3.9など)
整理後のフォルダ例
memo/
├ memo_sort.py
├ memo_sort.bat
└ 2026/
└ 202602/
├ 20260211/
│ └ 20260211_001 [調査]AWS .txt
└ 20260212/
└ 20260212_001 [SQL]顧客抽出 .sql
フォルダが散らかってきたタイミングで整理する程度で十分です。
なぜこの方法にしたか
以前は:
- 日付だけのファイル名
- 中身が分からない
- 探すのに時間がかかる
という状態でした。
タグを入れるようにしたことで、
ダブルクリックで実行できる
などで一発検索できるようになりました。
この運用のメリット
- tree検索が速い
- エディタに依存しない(プレーンテキスト)
- 軽い
- Pythonで後処理しやすい
個人的には Wiki やノートツールより気楽です。
今後やりたいこと
- タグの統一辞書
- SQLログだけ別管理
- 作業統計の自動化
同じようにテキストで業務ログ管理している方がいたら、
ぜひ運用方法を教えてください。
追記:
タグ付きメモの検索方法(環境別)
業務メモは [調査] や [SQL] のようにタグを付けて管理しています。
タグを付けておくと、環境に応じて様々な方法で検索できます。
Windows(エクスプローラ)
フォルダを開いて検索ボックスに入力:[調査]
ポイント:
• [] はそのままでOK
• ファイル名も本文も検索対象になります
一番手軽です。
Windows PowerShell
タグ検索
Select-String "\[調査\]" -Path *.txt -Recurse
ポイント:
• [ は正規表現のメタ文字なので、[ のようにエスケープして検索します。
• -Recurse でサブフォルダも検索
SQLだけ検索
Select-String "\[SQL\]" -Path *.sql -Recurse
コマンドプロンプト(cmd)
findstr
findstr /S /I "\[調査\]" *.txt
意味:
• /S サブフォルダ検索
• /I 大文字小文字無視
Windows標準で使えます。
Linux / macOS(grep)
基本検索
grep -r "\[調査\]" .
ファイル名だけ探す場合
find . -name "*[調査]*"
treeコマンドと組み合わせ
ファイル構造を見ながら探したい場合:tree | grep 調査
※ treeコマンドはファイル構造を一覧表示するコマンドです。
Windowsなら:tree /F | findstr 調査
個人的おすすめ運用
[調査]
[SQL]
[障害]
[作業]
のように括弧付きで検索すると、
• タグだけヒット
• 本文の単語と区別できる
ので探しやすくなります。
さらに追記:
momoフォルダをvscodeで開くと全文検索してくれるのでさらに便利
正規表現をONにして:
[調査]|[SQL]|[障害]|[作業]←複数検索
運用してみて分かったこと(追記)
実際にしばらく運用してみたところ、Markdown風ヘッダでタイトルやタグを毎回入力するのは少し手間に感じるようになりました。
現在は、サクラエディタでは「日付+連番」のシンプルなファイル名のみ自動生成し、Pythonで日付フォルダへ整理する元の形に戻しています。
なお、タグ管理の仕組み自体は有効だと感じているため、関連コードはコメントアウトした状態で残しておき、必要になったときに再利用できるようにしています。
さらに、VSCodeの全文検索を試したところ、クエリのエイリアス名や特徴的な文字列から過去メモを簡単に探せることも分かりました。タグを厳密に付けなくても、検索性能で十分カバーできるケースが多いと感じています。
結果として:
- ヘッダなしでも全文検索で十分見つかる
- 日付フォルダ整理だけでも時系列で追いやすい
- VSCode検索を併用すると過去ログ探索がさらに楽になる
- 記録のハードルが下がってメモが続きやすい
最初はタグ管理を重視していましたが、実務では「検索できること」と「継続できること」のバランスが大事だと実感しました。