Редактор и первый скрипт
Открываем редактор прямо из таблицы, пишем первую функцию, читаем журнал выполнения и проходим страшный экран «Google не проверил это приложение».
Привязанный (bound) скрипт — проект Apps Script, который живёт внутри конкретного документа: копируется вместе с ним, видит его через
getActiveSpreadsheet()и удаляется вместе с ним.
Где живёт редактор
Устанавливать не нужно ничего. Откройте любую Google-таблицу и выберите в меню Расширения → Apps Script. В новой вкладке откроется полноценная среда разработки — она работает в браузере и хранит проект в облаке.
Что вы там увидите:
- слева — файлы проекта. Файлы кода имеют расширение
.gs(Google Script), но внутри — обычный JavaScript; - по центру — редактор с заготовкой
function myFunction() {}; - сверху — выпадающий список функций и кнопки Выполнить и Отладка;
- снизу — журнал выполнения: сюда падают ваши логи и тексты ошибок;
- в левой панели — разделы Триггеры (расписания) и Журнал выполнения (история всех запусков, включая ночные автоматические).
Привязанный скрипт против отдельного проекта
Проект можно завести двумя способами, и разница между ними объясняет добрую половину вопросов новичков.
| Привязанный (bound) | Отдельный (standalone) | |
| Как создать | Расширения → Apps Script из документа | script.google.com → Новый проект |
| Видит документ-хозяин | да, getActiveSpreadsheet() вернёт его | нет, вернёт null |
| Копируется вместе с файлом | да | нет |
Может добавить меню в таблицу (onOpen) | да | нет |
| Как достучаться до таблицы | getActiveSpreadsheet() | SpreadsheetApp.openById('...') |
| Когда выбирать | автоматизация одной конкретной таблицы | библиотека, веб-приложение, скрипт над многими файлами |
Правило простое: скрипт про одну таблицу — делайте привязанным. Если один скрипт обслуживает десяток разных таблиц или это веб-приложение — берите отдельный проект и openById().
Самая частая жалоба новичка звучит так: «У меня getActiveSpreadsheet() возвращает null». В девяти случаях из десяти человек создал проект на script.google.com, а не из меню таблицы. Активной таблицы у такого проекта просто нет — и взяться ей неоткуда.
Первая функция
Сотрите заготовку и напишите вот это.
function privet() {
const tablica = SpreadsheetApp.getActiveSpreadsheet();
const imya = tablica.getName();
Logger.log('Таблица называется: %s', imya);
Logger.log('Листов внутри: %s', tablica.getSheets().length);
}Результат:
10:24:31 Выполнение начато
10:24:32 Информация Таблица называется: Заявки 2026
10:24:32 Информация Листов внутри: 3
10:24:32 Выполнение завершеноРазберём построчно.
function privet()— имя латиницей. Кириллицу JavaScript формально допускает, но выпадающий список редактора и триггеры с ней дружат плохо. Не рискуйте.SpreadsheetApp.getActiveSpreadsheet()— «дай таблицу, из которой открыт этот скрипт». Возвращает объектSpreadsheet, то есть книгу целиком, со всеми вкладками, а не отдельный лист. Это частая путаница.getName()— имя книги,getSheets()— массив её листов.Logger.log(...)— печать в журнал. Поддерживает подстановку через%s, так что склеивать строки плюсами необязательно.
Logger.log или console.log?
После перехода Apps Script на движок V8 работают оба, и оба пишут в журнал выполнения. Разница тонкая: console.log дополнительно уходит в Cloud Logging, поэтому его видно в истории выполнений даже для триггера, отработавшего ночью, а Logger.log исторически «локален» для текущего запуска. На практике: для отладки в редакторе годится любой, для скриптов на триггерах предпочитайте console.log.
А чего точно нет — так это alert(). Показать окно пользователю можно, но через сервис интерфейса:
function pokazatOkno() {
SpreadsheetApp.getUi().alert('Готово! Отчёт обновлён.');
}Работает это только тогда, когда скрипт запущен человеком из открытой таблицы — из меню или редактора. У скрипта, разбуженного триггером в три часа ночи, интерфейса нет, и такой вызов упадёт с ошибкой. Логи в этом случае — единственный способ узнать, что произошло.
Запуск и разрешения
Нажмите Сохранить — редактор не сохраняет автоматически, и без сохранения запустится прошлая версия кода (классическая ловушка потерянного часа). Затем выберите в выпадающем списке сверху функцию privet и нажмите Выполнить.
В первый раз Google остановит вас окном авторизации. Скрипт собирается трогать вашу таблицу — платформа обязана спросить разрешение. Дальше по шагам:
- Окно «Требуется авторизация» → Ознакомиться с разрешениями.
- Выбрать свой аккаунт Google.
- Экран «Google не проверил это приложение». Он пугает, но означает буквально следующее: этот код написали вы, и Google его не проверял, потому что он и не должен. Нажмите Дополнительные настройки → Перейти к проекту (небезопасно).
- Экран согласия перечисляет, что именно скрипт хочет: «Просмотр и управление таблицами в Google Диске». Нажмите Разрешить.
Это и есть OAuth-согласие: вы как владелец данных выдаёте своему же скрипту токен доступа. Спрашивают один раз на проект — до тех пор, пока список нужных прав не изменится. Допишете в код GmailApp.sendEmail — при следующем запуске Google снова попросит согласия, уже и на почту.
Откуда Google знает, какие права нужны? Он сканирует ваш код и находит упоминания сервисов. Увидел GmailApp — значит, нужен доступ к почте. Итоговый список прав можно посмотреть и зафиксировать вручную в манифесте appsscript.json (Настройки проекта → «Показывать файл манифеста appsscript.json»):
{
"timeZone": "Europe/Moscow",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets.currentonly"
]
}Поле timeZone выставьте сразу. От него зависит, в котором часу сработает триггер «каждый день в 9:00» и что вернёт new Date(). По умолчанию там вполне может оказаться американский часовой пояс — и отчёт «за вчера» начнёт приходить со сдвигом на день.
Как это работает
- Проект Apps Script — это набор файлов
.gsна серверах Google. У привязанного скрипта он хранится внутри документа (в его служебных данных), у отдельного — на вашем Диске. - Все файлы
.gsодного проекта делят одну глобальную область видимости. Никакихimportмежду ними не нужно: функция изUtils.gsпросто вызывается изMain.gs. Обратная сторона — две одноимённые функции в разных файлах молча затрут друг друга. - Нажатие «Выполнить» — это HTTP-запрос: браузер просит сервер выполнить одну конкретную функцию, выбранную в выпадающем списке. Не «весь файл».
- Токен OAuth привязан к паре «проект + пользователь». Коллега, который откроет вашу таблицу и запустит скрипт, пройдёт согласие заново — уже от своего имени и со своими правами. Ваши письма он при этом читать не сможет, а свои — сможет.
Частые ошибки
- Запускается не та функция. Вы написали
privet, а в списке сверху осталасьmyFunction— Google послушно выполнит пустую заготовку и бодро отрапортует «Выполнение завершено». Всегда проверяйте выпадающий список. - Забыли сохранить. Редактор не сохраняет сам. Ctrl+S (⌘+S) перед каждым запуском — рефлекс.
- Функция с параметрами. Функцию вида
function obrabotat(list)нельзя запустить кнопкой: аргумент взять неоткуда, иlistокажетсяundefined. Сделайте отдельную функцию-обёртку без параметров. - Испугались экрана «приложение не проверено» и закрыли вкладку. Без согласия скрипт не получит доступ, и все вызовы
SpreadsheetAppбудут падать. Через «Дополнительные настройки» пройти нужно — это ваш собственный код. - Ждут
getActiveSpreadsheet()в отдельном проекте. У проекта, созданного на script.google.com, активной таблицы нет. ТолькоopenById(). - Ищут логи триггера в редакторе. Журнал внизу показывает только текущий ручной запуск. История автоматических — в разделе Журнал выполнения слева.
Итоги
- Редактор открывается из таблицы: Расширения → Apps Script. Ставить ничего не нужно.
- Привязанный скрипт живёт внутри документа и видит его через
getActiveSpreadsheet(); отдельный проект — нет, ему нуженopenById(). getActiveSpreadsheet()возвращает книгу целиком, а не лист.- Логи —
Logger.log()илиconsole.log(); для триггеров надёжнееconsole.log().alert()не существует, естьSpreadsheetApp.getUi().alert(). - Первый запуск требует OAuth-согласия. Экран «Google не проверил это приложение» — нормально: приложение написали вы.
- Сохраняйте перед запуском и проверяйте, какая функция выбрана в выпадающем списке.