Содержание урока · 11 разделов
- Cobra completion: что появляется из коробки
- Куда положить скрипт для каждого шелла
- Установка в .bashrc и oh-my-zsh
- Что подсказывается автоматически
- Программная генерация при сборке
- --no-descriptions и цена описаний
- Версии bash и пакет bash-completion
- Что дальше
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Пользователь набрал notes, нажал Tab Tab — и увидел список команд вашей утилиты, набрал notes list -- , нажал Tab — увидел флаги. У kubectl, gh и hugo это работает с первого дня, хотя ни одна из команд не пишет скрипты автодополнения руками: их генерирует Cobra. Механика действительно «из коробки» — root-команда получает её автоматически при первом Execute, и пользователю не нужно ничего знать о вашем коде. При этом вокруг есть тонкости, которые легко пропустить: скрипт надо установить в правильное место конкретного шелла, в zsh без compinit дополнение тихо не заработает, а сгенерированный полгода назад скрипт ничего не знает о командах, добавленных вчера. Этот урок закрывает весь конвейер: от генерации до установки и поддержки.
Cobra completion: что появляется из коробки#
Как только root-команда исполнена, Cobra добавляет к ней команду completion с четырьмя подкомандами: bash, zsh, fish и powershell. Каждая печатает в stdout скрипт автодополнения — длинный, но не ваш: в нём функции конкретного шелла, которые при каждом Tab вызывают вашу же утилиту со скрытой командой __complete и списком уже введённых слов. Бинарник в ответ отдаёт варианты — команды, алиасы, флаги, — и шелл показывает их пользователю. Логика completions живёт в программе, а не в скрипте: добавили подкоманду, поменяли флаг — дополнение обновится на следующем запуске, перегенерировать скрипт заново не обязательно. У каждой подкоманды есть флаг --no-descriptions: подавляет пояснения рядом с вариантами, о нём ниже. Команда completion видна в справке наравне с остальными; если она не нужна — CompletionOptions.DisableDefaultCmd убирает её целиком, а HiddenDefaultCmd оставляет рабочей, но прячет из help.
Куда положить скрипт для каждого шелла#
| Шелл | Установка | Нюанс |
|---|---|---|
| bash | source <(notes completion bash) в .bashrc или файл в /etc/bash_completion.d | нужен bash-completion для старых версий |
| zsh | notes completion zsh > "${fpath[1]}/_notes" + compinit | без compinit работает молча и не подсказывает |
| fish | notes completion fish > ~/.config/fish/completions/notes.fish | подхватывает файлы из каталога автоматически |
| powershell | notes completion powershell | Out-String | Invoke-Expression в $PROFILE | выполняется при каждом старте шелла |
Bash: установка в .bashrc
GO# быстрый путь: вычислять скрипт при каждом старте шелла
echo 'source <(notes completion bash)' >> ~/.bashrc
# или сохранить один раз в каталог bash-completion:
notes completion bash > /etc/bash_completion.d/notesЗапись вида source <(...) вычисляет вывод команды как shell-скрипт при каждом запуске терминала — установка в одну строку, но бинарник обязан быть в PATH к моменту старта шелла. Файл в /etc/bash_completion.d подключается сам, если установлен пакет bash-completion; это путь дистрибутивов и пакетных менеджеров.
Установка в .bashrc и oh-my-zsh#
Bash — самый простой случай, его схема из блока выше закрывает 90% пользователей Linux. Zsh капризнее: скрипт должен лежать в каталоге из массива fpath под именем _notes — с подчёркиванием, совпадающим с именем команды, — а загрузчик функций compinit обязан отработать при старте. Если вы ставили oh-my-zsh, compinit вызывается его init-скриптом автоматически, и автодополнение bash zsh сводится к одной команде записи файла; в голом zsh проверьте, что .zshrc содержит autoload -Uz compinit и compinit, иначе дополнение молча не заведётся — шелл не ругается, просто Tab продолжает предлагать файлы каталога. Fish самый дружелюбный: положили файл в каталог completions — всё, перезапуск не обязателен. Powershell отличается тем, что скрипт исполняется в момент загрузки профиля, поэтому имя бинарника снова должно резолвиться в PATH заранее.
Zsh: файл _notes в fpath
GO# записать скрипт в первый каталог fpath:
notes completion zsh > "${fpath[1]}/_notes"
# затем перечитать конфиг или открыть новый шелл:
exec zshИмя файла обязано начинаться с подчёркивания и совпадать с именем команды: _notes — это соглашение zsh-загрузчика функций. Внутри oh-my-zsh compinit уже вызван, больше ничего делать не нужно. Обратите внимание на перезапись: каждая установка перебивает старый файл, поэтому обновлять дополнение — повторить одну команду.
Проверка: notes completion bash
Пользователь набрал notes completion bash в терминале и нажал Enter. Что произойдёт?
Что подсказывается автоматически#
Стоит понимать, за что скрипт отвечает без вашего участия. Подкоманды и их алиасы — да, включая группы и порядок из урока про группы и алиасы. Флаги — да: и локальные, и персистентные, унаследованные с корня, вместе с шорткатами и текстом помощи в описаниях. Значения булевых флагов true и false — тоже. А вот аргументы команд — нет: для них Cobra без ваших функций предложит файлы каталога, как и любой шелл. Поэтому notes show не подскажет имена заметок: для аргументов нужна ValidArgsFunction, тема следующего урока. Третье место, где дополнение умолчит — значения обычных строковых флагов: notes list --format <Tab> не знает, что format бывает json или table, и снова предложит файлы. Всё остальное обновится само при следующем запуске бинаря — перегенерация скрипта нужна только при обновлении самой Cobra.
Программная генерация при сборке#
Печатать скрипт руками — нормальный путь для пользователей. Но у команд, которые распространяются пакетами, есть второй: генерировать дополнение на этапе сборки и класть файлы прямо в пакет — так делает gh и почти всё, что живёт в brew и apt. В Go-коде это те же самые функции, которые стоят за подкомандами: GenBashCompletionV2, GenZshCompletion, GenFishCompletion, GenPowerShellCompletion. Обычно их прячут за служебную команду go generate или собирают отдельным бинарем, потому что в рантайме пользователю это не нужно. Дополнительный плюс генерации при сборке — версия скрипта всегда совпадает с версией бинарника: новая команда, добавленная в релизе, попадёт и в файл дополнения, который уехал в пакет. Как собрать релиз и проставить версии, разбирает урок про сборку и goreleaser.
Скрытая команда gen-completion
GOvar genCompletionCmd = &cobra.Command{
Use: "gen-completion",
Short: "Печатает скрипт автодополнения для bash",
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Root().GenBashCompletionV2(os.Stdout, true)
},
}
func init() {
rootCmd.AddCommand(genCompletionCmd)
}GenBashCompletionV2 принимает writer и includeDesc: true оставит описания команд в вариантах, false даст то же, что флаг --no-descriptions. Hidden прячет команду из справки — она нужна сборочному скрипту, а не человеку. Для остальных шеллов меняется одна строка: GenZshCompletion, GenFishCompletion, GenPowerShellCompletion с тем же writer.
Что ответит утилита
Прочитайте код и выберите, что он выведет:
rootCmd.CompletionOptions.DisableDefaultCmd = true
// пользователь запускает: notes completion zsh--no-descriptions и цена описаний#
По умолчанию __complete возвращает варианты вместе с описаниями: notes lis<Tab> подсказывает list с подписью «Показать заметки». Красиво, но не бесплатно: описания едут отдельным полем, и в zsh с сотнями вариантов второй столбец замедляет отрисовку списка и занимает экран. Флаг --no-descriptions на любой подкоманде completion — или DisableDescriptions в CompletionOptions — выключает подписи: останутся только имена. Третий сценарий — приватные утилиты, где описания раскрывают внутренности: путь команд ни к чему чужому глазу. Отдельно держите в голове рассинхрон версий: скрипт, сгенерированный старым бинарем, может не знать новых полей протокола __complete, поэтому правило простое — дополнение устанавливается из той же версии утилиты, которой пользуется человек. Обновили CLI — попросите пользователя перезаписать файл дополнения или обновите его в пакете при сборке.
Проверка: скрытая команда __complete
Зачем в дереве команд есть скрытая команда __complete?
Классический кейс: скрипт _notes записан в fpath, но compinit в .zshrc не вызван — либо вызван до того, как каталог попал в fpath. Симптом один: Tab работает, но предлагает файлы каталога, а не команды утилиты, без единого сообщения об ошибке. Второй вариант той же болезни — кэш загрузчика: команда переименована, а zsh продолжает предлагать старое имя, потому что прочитал ~/.zcompdump. Лечение: удалить ~/.zcompdump* и открыть новый шелл. Проверять это можно только в живом терминале — песочница шелл-скрипты не исполняет, здесь нужен локальный Go и свой zsh.
Версии bash и пакет bash-completion#
У bash два поколения скриптов. Новый V2 — тот, что печатает completion bash сегодня: он короче, быстрее и уважает директивы, но рассчитан на bash четвёртой ветки и установленный пакет bash-completion; на CentOS 7 и прочих раритетах его поведение непредсказуемо. Легаси-генератор GenBashCompletion остался для совместимости: скрипт крупнее, описаний не показывает, но работает даже там, куда новый не доезжает. Выбирают так: современные системы — V2, поддержка древних дистрибутивов — легаси. Проверить, что у пользователя, можно по секции bash в справке команды completion — Cobra печатает подсказки по установке с учётом версии генератора. Для fish и powershell таких поколений нет: один скрипт, одна схема установки из таблицы выше.
Спрячьте completion из справки
Впишите поле CompletionOptions и значение, при котором команда работает, но не видна в help.
Соберите main с настройкой completion
Расставьте: функция main, скрытие completion, вызов Execute с обработкой ошибки и выход.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: автодополнение
Что дальше#
Скрипт из этого урока подсказывает команды и флаги — но не аргументы: после notes show Tab предложит файлы, а не имена заметок. Это чинит ValidArgsFunction — следующий урок про динамические подсказки из файлов и API. Дополнение — часть «обвязки» CLI наравне с man-страницами: их генерацию разбирает урок про документацию, а упаковку скриптов в релиз — урок про сборку. Обзор курса — на странице Cobra. Практика на закрепление: установите дополнение для своего шелла, скройте completion из справки и проверьте, что Tab подсказывает и подкоманды, и флаги --no-descriptions против обычных.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Cobra добавляет команду completion с подкомандами bash, zsh, fish, powershell — скрипты печатаются в stdout.
- Сгенерированный скрипт вызывает скрытую команду __complete: логика дополнения живёт в бинарнике, новые команды подхватываются сами.
- Bash — source <(notes completion bash) в .bashrc; zsh требует файл _notes в fpath и вызов compinit.
- --no-descriptions выключает подписи у вариантов — быстрее в zsh и уместно для приватных утилит.
- DisableDefaultCmd удаляет команду целиком, HiddenDefaultCmd прячет из справки, оставляя рабочей.
- Дополнение ставится из той же версии бинаря: сгенерированный скрипт старой версии не знает новых команд.
Домашнее задание
Установите автодополнение своей утилиты для основного шелла: bash через .bashrc, zsh через файл в fpath, fish через каталог completions. Затем добавьте rootCmd.CompletionOptions.HiddenDefaultCmd и убедитесь, что completion исчез из справки, но работает. Сгенерируйте скрипт с --no-descriptions и сравните вид подсказок до и после.
Личные заметкиПусто
Вопросы и ответы по уроку
Как включить автодополнение для утилиты на Cobra?
Cobra генерирует команду completion автоматически: notes completion bash печатает скрипт, который нужно подключить — source <(notes completion bash) в .bashrc, для zsh сохранить в каталог fpath как _notes, для fish в ~/.config/fish/completions/. Логика подсказок живёт в бинарнике, поэтому после установки дополнение знает все команды и флаги.
Почему в zsh автодополнение не работает?
Чаще всего в .zshrc не вызван compinit, каталог со скриптом не попал в fpath или загрузчик держит устаревший кэш ~/.zcompdump. Симптом — Tab предлагает файлы вместо команд и не ругается. Проверьте порядок: сначала fpath, затем compinit; при переименовании команд удалите ~/.zcompdump* и откройте новый шелл.
Что делает флаг --no-descriptions?
Убирает пояснения справа от вариантов: остаётся только имя команды или флага. Полезно в zsh с длинными списками — отрисовка быстрее — и в приватных утилитах, где описания раскрывают внутренности. Тот же эффект глобально даёт rootCmd.CompletionOptions.DisableDescriptions.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Сравните с альтернативами
Практический вопрос после первых уроков: а почему именно эта библиотека? Мы разобрали конкурентов по одним критериям, с кодом одной задачи рядом и персональным подбором под ваши приоритеты.
Изучайте дальше
- Кастомные подсказки: ValidArgsFunction
- Генерация документации: man и markdown
- Тестирование команд CLI
- Структура большого CLI-проекта
- Красивый вывод: цвета и таблицы в терминале
- Интерактивный ввод: вопросы и подтверждения
- Ошибки и коды выхода: SilenceUsage
- Хуки PreRun и PostRun: цепочки выполнения
- Все курсы по 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