「コードは書けたけど、毎回エディタを開いて実行ボタンを押している」
これ、私がGASを覚えた直後にやっていたことです。自動化のつもりが、結局は手作業。トリガーを覚えて初めて、GASは勝手に働いてくれる道具になりました。
この記事では、トリガーの設定手順と、実際に運用してみて分かった落とし穴をまとめます。「設定したのに動かない」の原因は、だいたいこの記事のどこかに書いてあります。
トリガーの種類を先に整理する
GASのトリガーは、まず大きく2つに分かれます。ここを混同していると、いくら設定しても動きません。
| 種類 | 何がきっかけか | 設定方法 |
|---|---|---|
| 時間主導型 | 決めた時刻・間隔 | トリガー画面 or コード |
| イベント型 | 開く・編集・フォーム送信 | 同上(関数名でも自動判定) |
さらに、イベント型にはシンプルトリガーとインストーラブルトリガーという2種類があり、この違いが最初の関門です。
シンプルトリガーとインストーラブルトリガー
onOpen(e) onEdit(e) という名前で関数を書くと、設定しなくても勝手に動きます。これがシンプルトリガーです。手軽ですが、大きな制約があります。
- 権限が必要な処理ができない(メール送信、他ファイルへのアクセスなど)
- 実行時間が30秒までに制限される
つまり「編集されたらメールを送る」はシンプルトリガーでは動きません。ここで多くの人が「なぜか動かない」と悩みます。
権限が必要な処理をしたいときは、インストーラブルトリガー(トリガー画面から手動で登録するもの)を使います。同じ onEdit 相当の処理でも、関数名を handleEdit などに変えて、トリガー画面から「編集時」で登録すれば、メール送信もできるようになります。
時間主導型で選べる間隔
- 分ベース:1分・5分・10分・15分・30分おき
- 時間ベース:1・2・4・6・8・12時間おき
- 日ベース:毎日(時刻は「午前9時〜10時」のように1時間の幅で指定)
- 週ベース:曜日+時刻
- 月ベース:日付+時刻
分単位のきっちりした時刻指定はできません。「9時ちょうどに送りたい」は、この仕組みでは叶いません(後述の回避策はあります)。
設定手順:画面から登録する
Step1:動かす関数を用意する
function dailyReport() {
const sheet = SpreadsheetApp.openById('スプレッドシートのID').getSheetByName('日報');
sheet.appendRow([new Date(), '自動実行OK']);
}
ポイント:トリガーで動かす関数では SpreadsheetApp.getActive() を使わないでください。自動実行のときは「開いているシート」が存在しないため、null になって失敗します。必ず openById() でファイルを指定します。
Step2:手動で1回実行して権限を通す
いきなりトリガー登録をせず、まずエディタで手動実行します。初回はアカウントの選択と権限の許可が出るので、ここを済ませておきます。自動実行では許可画面を出せないため、この順番が大切です。
Step3:トリガー画面から登録する
- エディタ左メニューの**時計アイコン(トリガー)**をクリック
- 右下の「トリガーを追加」
- 実行する関数:
dailyReport - 実行するデプロイ:
Head - イベントのソース:時間主導型
- 時間ベースのトリガーのタイプ:日付ベースのタイマー
- 時刻:
午前9時〜10時 - **エラー通知設定:「今すぐ通知を受け取る」**に変更
- 保存
8番は既定が「1日おきに通知」になっていることがあります。壊れたその日に気づけるよう、必ず変えておいてください。
Step4:動くか確認する
翌朝まで待つ必要はありません。左メニューの**「実行数」**を開くと、いつ・どの関数が・成功したか失敗したかが一覧で見られます。失敗していれば、その行を開くとエラーの内容が読めます。
設定手順:コードから登録する
同じ設定をコードでも書けます。設定を他の人に渡すときや、設定をやり直すときはこちらが確実です。
/** トリガーをまとめて作り直す(1回だけ実行する) */
function setupTriggers() {
// 先に同じ関数の既存トリガーを削除する(重複防止)
const names = ['dailyReport', 'weeklyReport', 'watchStock'];
ScriptApp.getProjectTriggers().forEach(function (t) {
if (names.indexOf(t.getHandlerFunction()) !== -1) ScriptApp.deleteTrigger(t);
});
// 毎日9時台
ScriptApp.newTrigger('dailyReport').timeBased().atHour(9).everyDays(1).create();
// 毎週月曜8時台
ScriptApp.newTrigger('weeklyReport').timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY).atHour(8).create();
// 30分おき
ScriptApp.newTrigger('watchStock').timeBased().everyMinutes(30).create();
console.log('トリガーを3件登録しました');
}
/** 今登録されているトリガーを一覧で確認する */
function listTriggers() {
ScriptApp.getProjectTriggers().forEach(function (t) {
console.log(t.getHandlerFunction() + ' / ' + t.getEventType() + ' / ' + t.getTriggerSource());
});
}
「消してから作る」をセットにするのが重要です。作るだけのコードを何度か実行して、同じトリガーが5個並んでいた——そのぶん処理も5回走ります。私は集計が5重に登録されて、数字が5倍になったことがあります。
よくある失敗と対処
失敗1:トリガーは登録したのに動かない
まず「実行数」画面を見てください。ここに実行の記録があるのに失敗しているなら、原因は関数の中身です。エラー内容が表示されます。
記録がそもそも無いなら、トリガー自体が登録されていないか、対象の関数名が違います。listTriggers() で実際の登録内容を確認しましょう。
失敗2:手動では動くのにトリガーだと落ちる
原因のほとんどが SpreadsheetApp.getActive() です。openById() に変えてください。次に多いのが、後から追加したサービス(Gmail送信など)の権限を承認していないケース。一度手動実行して承認し直せば直ります。
失敗3:実行時刻がずれる
時間主導型は「指定した1時間のうちのどこか」で動きます。9時に設定しても9時47分に動くことがあります。仕様なので直せません。
どうしても時刻を絞りたいなら、短い間隔で動かして時刻で判定します。
function checkAndRun() {
const now = new Date();
// 9時0分〜9時9分の間だけ本処理を動かす
if (now.getHours() === 9 && now.getMinutes() < 10) {
dailyReport();
}
}
// → このcheckAndRunを「10分おき」のトリガーで回す
ただし実行回数が増えるので、1日の実行時間の上限(無料アカウントで90分)に注意してください。
失敗4:時刻が9時間ずれる
スクリプトのタイムゾーンが日本時間になっていません。エディタの**「プロジェクトの設定(⚙)」→ タイムゾーン**で「(GMT+09:00) 日本標準時 – 東京」を選んでください。古い解説にある「ファイル → プロジェクトのプロパティ」は、現在のエディタには存在しません。
失敗5:トリガーが増えすぎる
1つのスクリプトに登録できるトリガーは、ユーザーあたり20個までです。テストのたびに追加していると、ある日「トリガーを作成できません」と怒られます。定期的にトリガー画面を見て、使っていないものを削除しましょう。
失敗6:エラーに気づかない
自動実行は静かに失敗します。対策は2つ。
- トリガーのエラー通知を「今すぐ通知を受け取る」にする
- 処理を try / catch で囲み、失敗したら自分にメールする
function safeRun(fn, name) {
try {
fn();
} catch (e) {
GmailApp.sendEmail(Session.getActiveUser().getEmail(),
'【GAS】' + name + ' が失敗しました', e.message + '\n\n' + (e.stack || ''));
throw e; // 実行履歴にも失敗として残す
}
}
function main() {
safeRun(dailyReport, '日次レポート');
}
実運用で覚えたコツ
重い処理は夜中に置く
集計のような重い処理は、深夜や早朝に回しておくと、日中の実行枠を圧迫しません。私は「集計は3時台、通知は7時台」に分けています。
同じ時間帯に処理を固めない
同じファイルを触る処理が同時に走ると、書き込みが混ざることがあります。時間帯をずらすか、LockService で順番待ちさせます。
const lock = LockService.getScriptLock();
if (!lock.tryLock(10000)) return; // 10秒待って取れなければ諦める
try {
dailyReport();
} finally {
lock.releaseLock();
}
最初の1週間は毎日ログを見る
作った直後は、必ず「実行数」を毎日確認してください。私は最初の週に、想定していない時間に動いていたことに気づいて設定を直しました。動いているつもりで止まっているのが、自動化でいちばん怖い状態です。
私の使い方(看護師+GAS副業)
参考までに、実際に動かしているトリガーはこんな構成です。
- 毎朝7時台:その日の予定・やることをLINEに1通で送る
- 毎週日曜3時台:シートの古い行を「過去データ」へ退避する
- 毎月1日1時台:請求書のシートを当月分として複製する
夜勤で生活リズムが不規則なので、「決まった時間に自分に届く」仕組みがあるだけで、抜け漏れがかなり減りました。派手な自動化より、こういう地味なものほど長く働いてくれます。
まとめ
- イベント型にはシンプルとインストーラブルがあり、権限が要る処理は後者
- トリガーで動かす関数では
getActiveSpreadsheet()を使わない - 実行時刻は1時間の幅がある。分単位の指定はできない
- コードで登録するときは「消してから作る」
- トリガーは20個まで。エラー通知は「今すぐ」に
- 作った直後の1週間は「実行数」を毎日見る
トリガーを付けた瞬間から、書いたコードは「使う道具」ではなく「働き続ける仕組み」になります。まずは1本、いちばん面倒な作業に付けてみてください。