各フォルダの直下にあるファイル数を集計します。
親の行は子の件数を合算せず、全走査の合計は実行ログに分けて表示します。
ファイル件数は使用容量ではありません。
最終確認は2026年10月2日です。
ローカルの模擬検証を行いましたが、Googleへの認可・実シートへの反映・Driveの実ファイル列挙・Webアプリの配備は未検証です。
練習用の入力と設定
ZIPを展開し、使う記事のarticle-295.jsだけをApps Scriptへ保存します。
同名の関数を含むため、別の記事のコードを同じプロジェクトへ混ぜないでください。
| 設定名 | 設定する値 |
|---|---|
| FOLDER_ID | 閲覧できる練習フォルダID |
| SPREADSHEET_ID | 練習用スプレッドシートID |
| RECURSIVE | 直下だけはfalse。子フォルダも含める場合はtrue |
プロジェクトの設定でスクリプトプロパティを入力します。
まず新規の練習データを使い、要求される認可範囲と操作内容を確認してください。
入力例と実行する関数
親フォルダに1個、子に1個の練習例では、RECURSIVE=trueの結果に親1・子1が別行で出ます。
全走査合計は2です。
exportCountsで新しい結果シートを作ります。
// このコードは記事ごとに別のApps Scriptプロジェクトへ保存します。
function required_(key) {
const value=PropertiesService.getScriptProperties().getProperty(key);
if (!value) throw new Error(key+'を設定してください');
return value;
}
function safeCell_(value) {
const s=String(value);
return /^[=+@-]/.test(s) ? "'"+s : s;
}
function escapeHtml_(value) {
return String(value).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
}
function https_(value) {
const s=String(value).trim();
if (!/^https:\/\/[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::443)?(?:[/?#][^\s<>"'\\]*)?$/i.test(s)) throw new Error('HTTPS URLを確認してください');
return s;
}
function sourceSheet_() {
const sheet=SpreadsheetApp.openById(required_('SPREADSHEET_ID')).getSheetByName(required_('SHEET_NAME'));
if (!sheet) throw new Error('対象シートがありません');
return sheet;
}
function walkFolder_(root,recursive) {
const queue=[{folder:root,path:root.getName()}],seen=new Set(),rows=[],counts=[];
let visitedFiles=0;
for (let index=0;index<queue.length;index++) {
if (index>=100) throw new Error('100フォルダを超えるため中止しました');
const {folder,path}=queue[index],id=folder.getId();
if (seen.has(id)) throw new Error('重複フォルダIDです');
seen.add(id);
const files=folder.getFiles();let count=0;
while (files.hasNext()) {
const file=files.next();
if (++visitedFiles>1000) throw new Error('1000ファイルを超えるため中止しました');
rows.push([safeCell_(path),safeCell_(file.getName()),file.getUrl(),file.getId()]);count++;
}
counts.push([safeCell_(path),id,count]);
if (recursive) {
const children=folder.getFolders();
while(children.hasNext()) {
const child=children.next();queue.push({folder:child,path:path+' / '+child.getName()});
if(queue.length>100) throw new Error('100フォルダを超えるため中止しました');
}
}
}
return {rows,counts,total:visitedFiles};
}
function createOutput_(header,rows) {
// 走査完了後だけ、新規の空シートへ一括出力します。
const ss=SpreadsheetApp.openById(required_('SPREADSHEET_ID'));
const name='練習結果_'+Utilities.getUuid().replace(/-/g,'');
const sheet=ss.insertSheet(name);
if(sheet.getMaxRows()<rows.length+1) sheet.insertRowsAfter(sheet.getMaxRows(),rows.length+1-sheet.getMaxRows());
sheet.getRange(1,1,rows.length+1,header.length).setValues([header,...rows]);
return name;
}
function recursive_(){return PropertiesService.getScriptProperties().getProperty('RECURSIVE')==='true';}
function previewCounts() {
const result=walkFolder_(DriveApp.getFolderById(required_('FOLDER_ID')),recursive_());
Logger.log('フォルダ数: '+result.counts.length+' / 全走査ファイル数: '+result.total);return result;
}
function exportCounts() {
const result=previewCounts();
return createOutput_(['フォルダパス','フォルダID','直下のファイル数'],result.counts);
}
確認結果と切り分け
| 条件 | 結果 |
|---|---|
| 空フォルダ | フォルダの行を残して0を出力 |
| 子の件数 | 各行は直下の数。ログの全走査合計と区別 |
| 上限・重複ID | 途中の集計を完成結果として書き込まない |
Node.jsの模擬検証は5記事合計69ケースです。
純粋関数の結果と、模擬のGoogleオブジェクトで対象選択・書込先・失敗時停止を確認した範囲であり、実サービスの成功を示すものではありません。
100フォルダまたは1000ファイルを超える対象は走査を止めます。
この上限以内でもApps Scriptの実行時間や認可で停止する場合があります。
途中再開はなく、共有ドライブやショートカットのリンク先、見えないファイルを含む網羅的な棚卸しは未確認です。
ファイル名の先頭が数式として解釈される記号なら文字列として出力します。
Driveの共有範囲は変更せず、閲覧URLを取得しても他のユーザーへ閲覧権限を与えることにはなりません。
停止と戻し方
この例は定期トリガーを作成しません。
既存トリガーから呼ぶ変更を加える場合は、まず手動の1回を確認し、停止時にはそのトリガーを止めます。
結果シートは毎回新規です。
書込時の失敗で途中結果が残る場合は完成した一覧として使わず、既存の元データと混ぜないでください。
結果の削除や古いシートの一括消去はコードに含めていません。
公式資料
Driveフォルダの取得と列挙、シート範囲への反映、HTMLへの出力とエスケープ、Apps Scriptの利用上限を参照してください。
関連記事・旧実装の参考資料
従来のコードや外部の参考例は、本稿の練習ZIPと内容が異なる場合があります。
今回のコードは記事ごとに別のプロジェクトへ保存してください。




