Содержание урока · 11 разделов
Пользователь, открывший вашу утилиту впервые, делает одно и то же: печатает --help и за десять секунд решает, разбираться дальше или закрыть терминал. За эти десять секунд отвечает не код команды, а страница справки, и у Cobra она собрана из двух слоёв. Первый слой — содержимое: поля Short, Long и Example, которые вы заполняете у каждой команды. Второй — форма: два Go-шаблона, HelpTemplate и UsageTemplate, раскладывающие это содержимое по экрану. Пока утилита маленькая, дефолтная страница выглядит прилично, и трогать её не нужно. Но как только справка перестаёт вмещать нужное — пример с флагами, секция про переменные окружения, особый формат для одной команды — начинается кастомизация. Пройдём оба слоя по порядку: сначала поля, из которых складывается текст страницы, затем шаблоны, задающие ей форму, и в конце — функция SetHelpFunc, заменяющая рендеринг целиком.
Две страницы: help и usage#
У каждой команды в Cobra две страницы, и это разные тексты. Usage — выжимка: строка использования UseLine, алиасы, список доступных подкоманд, локальные и глобальные флаги. Help — то, что печатается по флагу --help и по команде help: сначала абзац Long, затем Example, а в конце — usage-страница целиком. Из этого устройства следует главное правило кастомизации: содержимое пишем в поля, а не в шаблоны — Long и Example попадают на страницу сами, шаблоны нужны, когда не устраивает раскладка. Usage чаще всего видит расстроенный пользователь: при ошибке разбора флагов Cobra печатает сообщение и следом usage-страницу, и простыня флагов в этот момент скорее мешает найти саму ошибку — как её убрать, разбирается в уроке про ошибки и коды выхода. Отрендеренную usage можно получить и из кода: метод UsageString() возвращает её строкой, чем пользуются и кастомные шаблоны, и тесты.
Long и Example: содержимое страницы
GOvar serveCmd = &cobra.Command{
Use: "serve [каталог]",
Short: "Запустить сервер заметок",
Long: `Запускает HTTP-сервер, который раздаёт заметки из каталога.
Порт и каталог переопределяются флагами; конфиг
читается из текущего каталога.`,
Example: ` notes serve # текущий каталог
notes serve ~/notes --port 9000
notes serve --open # сразу открыть браузер`,
RunE: func(cmd *cobra.Command, args []string) error {
return startServer(cmd, args)
},
}Long — абзац для страницы help: не дублируйте Short дословно, здесь место для деталей, которых не видно в списке команд. Example — многострочная строка с реальными вызовами; отступ в два пробела задаёт выравнивание, как в справках kubectl и gh. Оба поля дефолтный шаблон печатает сам — пока они заполнены, в шаблоны лезть не нужно.
Проверка: пустой Long
У serveCmd не заполнено поле Long, Short задан. Что напечатает notes serve --help?
Кастомизация help: шаблон SetHelpTemplate#
Шаблон help — обычный text/template из стандартной библиотеки: в двойных фигурных скобках доступны поля команды, и на них можно собрать что угодно. Минимальная осмысленная замена дефолту — SetHelpTemplate с телом из описания и usage: {{.Long}}, пустая строка, {{.UsageString}}. В шаблоне доступны .Long, .Short, .Example, .CommandPath, .UseLine, .Runnable, флаги через .LocalFlags и .InheritedFlags, а из функций — rpad, gt, eq и trimTrailingWhitespaces, которыми пользуется и дефолтный шаблон. Важная деталь механики: шаблон ищется не только на самой команде. Не нашли у подкоманды — Cobra поднимается по дереву и берёт шаблон ближайшего предка, у которого он задан; установили на корне — получили для всего дерева сразу. И следите за концом шаблона: печатается ровно то, что в нём написано, включая финальный перенос строки, — без него справка прилипнет к следующему приглашению терминала.
SetHelpTemplate с секцией примеров
GOfunc init() {
rootCmd.SetHelpTemplate(`{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
{{end}}{{if .HasExample}}Примеры:
{{.Example}}
{{end}}{{.UsageString}}`)
}Шаблон начинается как дефолтный — or .Long .Short с обрезкой хвостовых пробелов, — затем идёт своя секция «Примеры:», печатающаяся только при заполненном Example, и в конце UsageString со списком команд и флагов. Пустые строки между секциями набраны руками: шаблон ничего не добавляет сам, поэтому переводы строк — часть вашего контракта с пользователем.
Шаблон, который печатает один Long
Прочитайте код и выберите, что он выведет:
rootCmd.SetHelpTemplate("{{.Long}}")
// notes serve --help
// Long и Example у serveCmd заполненыUsage-шаблон: SetUsageTemplate и секции#
Usage-шаблон устроен плотнее: дефолт собирает строку использования, алиасы, примеры, доступные команды, флаги, глобальные флаги и дополнительные темы помощи — каждая секция через своё условие {{if}}. Кастомизация здесь обычно точечная: русифицировать заголовок Available Commands, спрятать секцию Additional help topics, добавить в конец строку со ссылкой на документацию. Механика наследования та же, что у help-шаблона: SetUsageTemplate на корне действует на всё дерево, а отдельная команда со своим шаблоном переопределяет только себя. Совсем заменить рендеринг функцией можно методом SetUsageFunc — она принимает func(*cobra.Command) error и пригодится, когда странице нужно то, чего шаблоном не выразить, например вывод через внешнюю программу. На практике функций хватает одному проценту утилит: сначала пробуйте шаблон, и только упершись в его пределы — SetUsageFunc.
Свой usage-шаблон по-русски
GOrootCmd.SetUsageTemplate(`Usage:{{if .Runnable}}
{{.UseLine}}{{end}}
Доступные команды:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}
Флаги:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
`)Упрощённый usage: строка использования, команды с русским заголовком и локальные флаги. Range по .Commands с проверкой IsAvailableCommand оставляет видимыми только настоящие команды плюс help, rpad выравнивает имена по общей ширине. За основу берите полный дефолтный шаблон из исходников своей версии Cobra — он длиннее и включает глобальные флаги и темы помощи.
SetHelpFunc: справка под полный контроль#
Когда шаблонов мало, остаётся функция. SetHelpFunc принимает func(*cobra.Command, []string) и полностью забирает рендеринг: Cobra больше ничего не печатает, всё на вас — хоть JSON для скриптов, хоть вывод через пейджер вроде less, хоть запрос к внешней программе. Аргументы — сама команда и хвост, который пользователь дописал после слова help: вызов notes help serve принесёт в функцию serve. Внутри принято печатать через cmd.OutOrStdout(): при обычном запуске это stdout, а в тестах вывод перехватывается буфером — им же мы будем ловить справку в уроке про тестирование. Наследование работает и здесь: HelpFunc ищется по дереву вверх, и функция корня отрисует справку всем подкомандам, у которых своей нет. Скажу честно, как о рабочем опыте: в девяти проектах из десяти хватает Long, Example и одного шаблона, а SetHelpFunc остаётся лазейкой на особые случаи — держите её в голове, но не открывайте без причины.
SetHelpFunc: своя функция справки
GOrootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
out := cmd.OutOrStdout()
if cmd.Long != "" {
fmt.Fprintln(out, cmd.Long)
} else {
fmt.Fprintln(out, cmd.Short)
}
if cmd.Example != "" {
fmt.Fprintf(out, "
Примеры:
%s
", cmd.Example)
}
fmt.Fprint(out, cmd.UsageString())
})Функция повторяет дефолтную логику в миниатюре: описание с подстановкой Short, свои «Примеры», usage в конце. Вся печать через OutOrStdout — справка уважает перенаправление и тестовые буферы. Обратите внимание: функция получает указатель на конкретную команду, поэтому никакой хардкод «описания корня» внутри недопустим — читайте cmd.
Проверка: чья функция справки
Справку рендерит функция, заданная через rootCmd.SetHelpFunc. У serveCmd собственной help-функции нет. Что произойдёт при notes serve --help?
Флаг --help у каждой подкоманды#
Флаг -h/--help не объявляется руками: Cobra добавляет его каждой команде сама, через InitDefaultHelpFlag, в момент выполнения. Поэтому notes serve --help показывает справку именно serve: её Long, её Example, её флаги, а флаги корня попадают в отдельную секцию Global Flags. Help-команда тоже строится автоматически, и notes help serve приводит к той же странице, что и notes serve --help, — разница только в привычке. Для --help не выполняется ни Run, ни хуки из следующего урока: проверка флага случается раньше, поэтому справка открывается мгновенно даже у тяжёлых команд, и звать её из скриптов безопасно. Если понадобится своё поведение помощи, команду help можно подменить через SetHelpCommand — но это экзотика, и в девяти случаях из десяти штатного флага достаточно.
Самая коварная правка справки — SetHelpTemplate с телом только из {{.UsageString}}: страница выглядит нормально, команды и флаги на месте, только Long и Example исчезли. Замечают это через месяц, когда в issue спрашивают, куда делись примеры. Вторая ловушка — незакрытая {{if}} или опечатка в поле: ошибка вылезет не при компиляции, а в рантайме при каждом вызове --help, и пользователь вместо справки увидит сообщение о template-ошибке. После любой правки шаблона соберите утилиту и прогоните --help для корня и пары подкоманд руками — песочница терминальный вывод не эмулирует, здесь нужен локальный Go.
Чек-лист справки перед релизом#
- Long есть у каждой команды с флагами; Short описывает команду одним предложением и не повторяет Long дословно.
- Example показывает два-три реальных вызова, начиная с самого частого сценария.
- --help проверен на трёх уровнях: корень, подкоманда, вложенная подкоманда; отдельно — вызов notes help serve.
- Перенаправление вывода не ломает справку: она уходит через OutOrStdout, а не печатается мимо stdout.
- Правка шаблона не тронула автодополнение и команду help — они живут отдельно от шаблонов.
help и usage рядом#
| help | usage | |
|---|---|---|
| Когда печатается | по --help/-h и команде help | после ошибки флагов и внизу help-страницы |
| Состав | Long или Short, Example, usage целиком | UseLine, алиасы, команды, флаги |
| Кастомизация | SetHelpTemplate и SetHelpFunc | SetUsageTemplate и SetUsageFunc |
| Наследование | у ближайшего предка с шаблоном или функцией | то же самое |
| Доступ из кода | cmd.Help() печатает страницу | cmd.UsageString() возвращает строкой |
Соберите шаблон и описание
Впишите название шаблонного метода и поле длинного описания.
Соберите SetHelpFunc из шести строк
Расставьте: переменная корня, начало функции, печать описания, печать usage, закрытие функции, регистрация.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: шаблоны и функции справки
Что дальше#
Страница справки собрана, и в следующем уроке про хуки PreRun и PostRun мы переходим к тому, что происходит вокруг выполнения команды: цепочка PersistentPreRunE → PreRunE → RunE → PostRunE и знаменитая ловушка наследования PersistentPreRun. Справка — идеальный кандидат на тест: в уроке про тестирование команд вывод --help ловится буфером и сравнивается с ожидаемым, так что правки шаблона перестанут быть слепыми. Обзор всех тем курса — на странице Cobra. И напоминание про среду, уже знакомое по другим урокам курса: шаблоны исполняются только живой утилитой, поэтому соберите проект локально, вызовите --help для корня и пары подкоманд и прочитайте результат своими глазами раньше, чем это сделают пользователи.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Справка состоит из двух слоёв: содержимое в полях Long и Example, форма — в шаблонах HelpTemplate и UsageTemplate.
- Help печатается по --help и команде help; usage показывается после ошибок флагов и внизу help-страницы.
- Пустой Long подменяется Short — заполняйте Long хотя бы у команд с флагами.
- SetHelpTemplate на корне действует на всё дерево: шаблон ищется у ближайшего предка с заданным значением.
- UsageString() возвращает usage строкой — удобно для своих шаблонов и тестов.
- SetHelpFunc заменяет рендеринг целиком; вся печать — через cmd.OutOrStdout().
- После правки шаблона проверяйте --help локально: ошибка шаблона вылезает только в рантайме.
Домашнее задание
Заполните Long и Example для трёх команд своей утилиты, затем замените заголовок Available Commands на «Доступные команды» через SetUsageTemplate. Добавьте секцию «Примеры:» в SetHelpTemplate так, чтобы она печаталась только при непустом Example. Соберите проект, откройте --help для корня и двух подкоманд и проверьте, что ни одна секция не потерялась.
Личные заметкиПусто
Вопросы и ответы по уроку
Как изменить заголовок Available Commands в Cobra?
Задайте свой SetUsageTemplate на корне: скопируйте дефолтный шаблон из исходников Cobra вашей версии и замените текст заголовка внутри range по .Commands. Дефолтный шаблон меняется между версиями, поэтому правьте копию, а не пересобирайте с нуля.
Чем SetHelpFunc отличается от SetHelpTemplate?
Шаблон — это text/template, который Cobra исполняет за вас; функция — полный контроль: она вызывается вместо любого рендеринга и печатает что угодно, включая пейджер или JSON. Для большинства утилит хватает шаблона, функция нужна для особых форматов вывода.
Почему --help подкоманды показывает Short вместо длинного описания?
Поле Long у этой подкоманды пустое: дефолтный шаблон берёт or .Long .Short. Заполните Long — и страница подкоманды получит полноценное описание, не трогая Short в списке команд родителя.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Сравните с альтернативами
Практический вопрос после первых уроков: а почему именно эта библиотека? Мы разобрали конкурентов по одним критериям, с кодом одной задачи рядом и персональным подбором под ваши приоритеты.
Изучайте дальше
- Хуки PreRun и PostRun: цепочки выполнения
- Ошибки и коды выхода: SilenceUsage
- Интерактивный ввод: вопросы и подтверждения
- Красивый вывод: цвета и таблицы в терминале
- Приоритеты конфигурации: флаг, env, файл
- Переменные окружения: AutomaticEnv
- Конфиг-файлы: YAML, TOML и JSON
- Знакомство с Viper: конфигурация без боли
- Все курсы по Go
Следующая ступень учебного пути
Bubble Tea: TUI-приложения в терминале
Интерактивный курс Bubble Tea — TUI-фреймворка на Go: Elm-архитектура, bubbles, Lip Gloss, мышь и клавиатура. 25 уроков с примерами и итоговым приложением.
- Понимать Elm-архитектуру: Model, Update, View, Init
- Обрабатывать клавиатуру и мышь в терминале
- Писать асинхронные операции через tea.Cmd, Batch и Sequence
- Использовать bubbles: list, textinput, textarea, table, viewport, progress, spinner