各フォルダの直下にあるファイル数を集計します。
親の行は子の件数を合算せず、全走査の合計は実行ログに分けて表示します。
ファイル件数は使用容量ではありません。
以下の模擬検証は2026年10月2日の記録です。
ローカルの模擬検証を行いましたが、Googleへの認可・実シートへの反映・Driveの実ファイル列挙は未検証です。
この集計はApps Scriptエディタから実行し、Webアプリの配備は不要です。
2026年10月25日に公式資料と配布ZIPを照合し、article-295.jsと本文コードの一致を確認しました。
今回はGoogle上での実行や画面確認を行っていません。
練習用の入力と設定
実行するGoogleアカウントで閲覧できるフォルダと、編集できる練習用スプレッドシートを用意します。
出力先スプレッドシートを集計対象フォルダ内に置くと、それ自体もファイル数に含まれます。
ZIPを展開し、article-295.jsの内容を新規Apps Scriptプロジェクトのコードへ貼り付けて保存します。
同名の関数を含むため、別の記事のコードを同じプロジェクトへ混ぜないでください。
| 設定名 | 設定する値 |
|---|---|
FOLDER_ID | 閲覧できる練習フォルダID |
SPREADSHEET_ID | 練習用スプレッドシートID |
RECURSIVE | 直下だけはfalse。子フォルダも含める場合はtrue |
左側の「プロジェクトの設定」を開き、「スクリプト プロパティ」に表の3項目を追加して保存します。
RECURSIVEは小文字のtrueまたはfalseを、引用符を付けずに入力してください。
掲載コードは文字列trueとの完全一致で判定するため、未設定やTRUEでは子フォルダを調べません。
コード中にあるSHEET_NAMEは、この集計で呼ばれない共通関数の設定なので入力不要です。
追加・保存の場所はGoogle公式のプロパティ設定手順を参照してください。
まず新規の練習データを使い、要求される認可範囲と操作内容を確認してください。
GASの構文から学びたい場合は、次の入門書も候補です。
このサンプルを試すために購入する必要はなく、まず本文と公式資料で動きを確かめてください。
入力例と実行する関数
親フォルダに1個、子に1個の練習例では、RECURSIVE=trueの結果に親1・子1が別行で出ます。
全走査合計は2です。
最初にpreviewCountsで集計だけを行い、内容を確かめてからexportCountsで出力します。
- エディタでコードを保存し、関数の選択欄から
previewCountsを選んで「実行」を押します。 - 初回に認可が求められたら、実行アカウントと要求されるアクセス範囲を確認します。
- 実行ログで、練習例のフォルダ数が2、全走査ファイル数が2になるか確かめます。
- エラーがなければ
exportCountsを選んで実行し、指定したスプレッドシートを開きます。 - 新しい
練習結果_で始まるシートに、親と子の行がそれぞれ1件と出るか確認します。
previewCountsでは結果シートを作りませんが、対象フォルダの走査は実行します。
exportCountsは再び走査するため、その間にファイルが増減すると件数も変わります。
出力列はAがフォルダパス、BがフォルダID、Cが直下のファイル数で、個別ファイル名の一覧は出力しません。
操作の基本はエディタからの関数実行とGoogleサービスの認可を参照してください。
// このコードは記事ごとに別の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ファイルを超えると、掲載コードが走査を止めます。
これはサンプル独自の上限で、Google Drive全体の保存上限ではありません。
公式のApps Script実行時間は1回6分で、この件数以内でも時間切れや認可エラーになる場合があります。
途中再開はなく、共有ドライブやショートカットのリンク先、見えないファイルを含む網羅的な棚卸しは未確認です。
出力するフォルダパスの先頭が=などの記号なら、文字列として扱うようにしています。
Driveの共有範囲は変更せず、閲覧URLを取得しても他のユーザーへ閲覧権限を与えることにはなりません。
設定不足のエラーが出たら、メッセージにあるプロパティ名の綴りと保存済みの値を確認します。
フォルダ取得や出力で権限エラーになる場合は、同じ実行アカウントで対象を開けるか確認してください。
子の行が出ないときはRECURSIVE、件数が想定と違うときはフォルダIDと出力先の置き場所を見直します。
件数上限や時間切れなら、対象を小さな子フォルダに絞って実行し直します。
件数を照合するときに残す記録
実行前に、親と子の直下にある練習ファイルを別々に数えて控えてください。
出力先シートは対象の外へ置き、集計中は練習ファイルの追加や移動を止めます。
実行日時・対象フォルダID・RECURSIVEの値・ログの件数を記録しましょう。
出力後はフォルダ名だけでなくIDも照合し、同名の別フォルダを数えていないか確認します。
親の行を子の合計と思って比較すると、正常な結果でも不足に見える点に注意してください。
一致しない場合は、直下だけの実行から始めて、子を含める条件に一つずつ広げます。
広い範囲でエラーが出たときは、上限値を増やす前に小さい対象で完了するかを確認してください。
この記録を残すと、設定の違いと走査中のファイル増減を分けて調べやすくなります。
停止と戻し方
この例は定期トリガーを作成しません。
既存トリガーから呼ぶ変更を加える場合は、まず手動で1回確認します。
定期実行を終了するときは、対象のトリガーを削除します。
結果シートは毎回新規です。
書込時の失敗で途中結果が残る場合は完成した一覧として使わず、既存の元データと混ぜないでください。
結果の削除や古いシートの一括消去はコードに含めていません。
公式資料
Driveフォルダの取得と列挙、シート範囲への反映、HTMLへの出力とエスケープ、Apps Scriptの利用上限を参照してください。
関連記事・旧実装の参考資料
従来のコードや外部の参考例は、本稿の練習ZIPと内容が異なる場合があります。
今回のコードは記事ごとに別のプロジェクトへ保存してください。




まとめ:小さなフォルダで直下と全走査の数を照合する
まず練習用の親・子フォルダでログと各行の数を照合し、合ってから対象を広げてください。
