Driveファイルの版番号が混ざる前に|GASで名前と更新日をdry-run監査
ツールおじ|GoogleアカウントをAIエージェント化
「最終版」「修正版」「v2」が同じフォルダに増え、どれを残すべきか分からない。そんな状態で名前変更や削除を始めると、必要な版まで触ってしまいます。
この記事では、Google Driveのファイル一覧をまとめたシートを読み取り、版番号のない行、同じ版番号の重複、更新日時と版番号の逆転をGASで確認します。元シート、Drive上のファイル、共有権限は変更しません。
先に知っておきたいこと
- できること: ファイル名の末尾にある v1、v1.2、v03 などを読み取り、行番号と問題種別だけをログへ出します。
- できないこと: 正しい最新版の自動決定、ファイル名の変更、古い版の削除、フォルダ移動は行いません。
- 必要なもの: Googleスプレッドシートと、その編集権限です。Driveの変更権限は不要です。
- 安全設計: 初期値は dry-run 固定です。ファイル名、ファイルID、共有先メールアドレスはログへ出しません。
版番号は、ファイル名の末尾に「_v2」「-v2.1」「 v03」のように付いている前提です。拡張子がある場合も確認できます。
準備するシート
シート名を FileInventory にして、1行目へ次の3列を用意します。
- file_id: ファイルを区別するためのID。空欄はHOLDになります。
- file_name: 例「企画書_v2.1.pdf」。この値はログへ出しません。
- updated_at: 更新日時。スプレッドシートの日時形式で入力します。
まずはコピーしたスプレッドシートへ5〜10件だけ入れて試してください。元の台帳を直接使う必要はありません。
この監査で見つけるもの
- VERSION_MISSING: ファイル名から版番号を読み取れない
- DUPLICATE_VERSION: 同じ資料名と版番号の組み合わせが複数ある
- UPDATE_ORDER_MISMATCH: 大きい版番号の更新日時が、小さい版番号より古い
- INPUT_INVALID: 必須列の空欄、日時不正、行数上限超過
監査結果は行番号と問題種別だけをログへ出します。ファイル名そのものをログへ残さないため、共有ログから案件名や顧客名が見えるリスクを抑えられます。
コピーして使える最小コード
function auditDriveVersionNamesFree() {
const config = {
sheetName: 'FileInventory',
dryRun: true,
maxRows: 500,
expectedHeaders: ['file_id', 'file_name', 'updated_at'],
};
if (config.dryRun !== true) {
throw new Error('安全のため dryRun: true のときだけ実行できます。');
}
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName(config.sheetName);
if (!sheet) {
throw new Error('FileInventory シートが見つかりません。');
}
const values = sheet.getDataRange().getValues();
if (values.length < 2) {
throw new Error('見出し行の下に確認用データを入れてください。');
}
const headers = values[0].map(function (value) {
return String(value).trim().toLowerCase();
});
const missingHeaders = config.expectedHeaders.filter(function (header) {
return headers.indexOf(header) === -1;
});
if (missingHeaders.length > 0) {
throw new Error('不足している列: ' + missingHeaders.join(', '));
}
const body = values.slice(1);
if (body.length > config.maxRows) {
throw new Error('一度に確認できるのは ' + config.maxRows + ' 行までです。');
}
const column = {};
config.expectedHeaders.forEach(function (header) {
column[header] = headers.indexOf(header);
});
const issues = [];
const versionGroups = {};
body.forEach(function (row, offset) {
const rowNumber = offset + 2;
const fileId = String(row[column.file_id] || '').trim();
const fileName = String(row[column.file_name] || '').trim();
const updatedAt = toValidDate_(row[column.updated_at]);
if (!fileId || !fileName || !updatedAt) {
issues.push({ row: rowNumber, type: 'INPUT_INVALID' });
return;
}
const parsed = parseVersionName_(fileName);
if (!parsed) {
issues.push({ row: rowNumber, type: 'VERSION_MISSING' });
return;
}
const groupKey = normalizeStem_(parsed.stem);
if (!versionGroups[groupKey]) {
versionGroups[groupKey] = [];
}
versionGroups[groupKey].push({
row: rowNumber,
version: parsed.version,
versionText: parsed.versionText,
updatedAt: updatedAt,
});
});
Object.keys(versionGroups).forEach(function (groupKey) {
const records = versionGroups[groupKey];
const seenVersions = {};
records.forEach(function (record) {
if (!seenVersions[record.versionText]) {
seenVersions[record.versionText] = [];
}
seenVersions[record.versionText].push(record.row);
});
Object.keys(seenVersions).forEach(function (versionText) {
const rows = seenVersions[versionText];
if (rows.length > 1) {
rows.forEach(function (rowNumber) {
issues.push({ row: rowNumber, type: 'DUPLICATE_VERSION' });
});
}
});
const byVersion = records.slice().sort(function (a, b) {
return a.version - b.version;
});
for (let index = 1; index < byVersion.length; index += 1) {
const olderVersion = byVersion[index - 1];
const newerVersion = byVersion[index];
if (newerVersion.updatedAt.getTime() < olderVersion.updatedAt.getTime()) {
issues.push({ row: newerVersion.row, type: 'UPDATE_ORDER_MISMATCH' });
}
}
});
const uniqueIssues = deduplicateIssues_(issues);
const counts = uniqueIssues.reduce(function (result, issue) {
result[issue.type] = (result[issue.type] || 0) + 1;
return result;
}, {});
console.log(JSON.stringify({
dryRun: true,
checkedRows: body.length,
issueCount: uniqueIssues.length,
counts: counts,
issues: uniqueIssues,
privacy: 'ファイル名・ファイルID・メールアドレスはログへ出していません',
}));
}
function parseVersionName_(fileName) {
const match = fileName.match(/^(.*?)[_\-\s]+v(\d+)(?:\.(\d+))?(\.[^.]+)?$/i);
if (!match) return null;
const major = Number(match[2]);
const minor = Number(match[3] || 0);
if (!Number.isInteger(major) || !Number.isInteger(minor)) return null;
return {
stem: match[1],
version: major * 1000 + minor,
versionText: major + '.' + minor,
};
}
function normalizeStem_(stem) {
return String(stem)
.normalize('NFKC')
.trim()
.replace(/\s+/g, ' ')
.toLowerCase();
}
function toValidDate_(value) {
const date = value instanceof Date ? value : new Date(value);
return Number.isNaN(date.getTime()) ? null : date;
}
function deduplicateIssues_(issues) {
const seen = {};
return issues.filter(function (issue) {
const key = issue.row + ':' + issue.type;
if (seen[key]) return false;
seen[key] = true;
return true;
}).sort(function (a, b) {
return a.row - b.row || a.type.localeCompare(b.type);
});
}実行手順
- 確認対象とは別のスプレッドシートを作り、FileInventory シートと3列を用意します。
- 5〜10件のサンプルを入力します。版番号あり、版番号なし、同じ版番号を混ぜると挙動を確認しやすくなります。
- 拡張機能から Apps Script を開き、コードを貼り付けます。
- auditDriveVersionNamesFree を選んで実行します。
- 実行ログの issueCount、counts、issues を確認します。
- 問題行は元データ側で人が確認し、必要なファイルだけを手動で整理します。
初回はスプレッドシートを読み取る権限確認が表示されます。Drive API、Gmail、外部サービスへの接続はありません。
ログの読み方
たとえば issues に row 6、type DUPLICATE_VERSION と出たら、FileInventory の6行目を確認します。ログだけを見て削除や名前変更を決めず、同じ資料名の前後版と内容を開いて比較してください。
issueCount が0でも、版番号の付け方がチーム内で統一されていることまでは保証できません。v2、改訂版、最終版が混在する場合は、先に命名ルールを決める必要があります。
失敗しやすい点と制限
- 「最終版」「確定」「修正版」だけの名前は版番号として扱いません。
- v1.2.3 のような3段階の番号には未対応です。
- 同じ資料かどうかは、版番号を除いたファイル名で判断します。表記ゆれが大きい場合は別グループになります。
- 更新日時は編集や再アップロードでも変わるため、版の正しさを断定する材料にはできません。
- 500行を超える場合は、対象フォルダや期間を分けてください。
- 共有ドライブやショートカット固有の権限状態は確認しません。
- 自動削除、自動移動、自動リネームは実装していません。
公開前チェックリスト
- 元のDriveファイルや台帳を変更していない
- ファイル名、ID、共有先メールアドレスをログへ出していない
- 問題行を人が確認してから整理する手順になっている
- dryRun が true のままになっている
- 少量のコピー環境で先に試している
まとめ
版管理で最初に必要なのは、自動削除ではなく「どの行を人が確認するか」を安全に絞ることです。このコードは、版番号の不足・重複・更新順の逆転を、元データ非変更で確認します。
Drive内のファイル一覧を作るところから効率化したい場合は、次の有料Tipsが関連します。
DriveFileList Pro|Google Driveのファイル一覧化GASテンプレ

GasおじのTips商品一覧を見る

※学習・検証用の例です。実データへ適用する前に、コピー環境と少量データで確認してください。Google公式・Google公認の商品ではありません。
