---
title: "Автодополнение: bash, zsh, fish, powershell"
description: "Cobra completion из коробки: автодополнение bash zsh fish powershell, установка в .bashrc и oh-my-zsh, --no-descriptions и подводные камни."
course: "Cobra: CLI-приложения на Go"
url: "https://golangmaster.ru/courses/cobra/lessons/17-autocompletion/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 18
---

# Автодополнение: bash, zsh, fish, powershell

*Курс «Cobra: CLI-приложения на Go», урок 17 из 25. ~18 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/cobra/lessons/17-autocompletion/*

Cobra completion — это команда, которую фреймворк добавляет в ваш CLI бесплатно: одна строка в шелле, и автодополнение bash zsh fish powershell подсказывает подкоманды, флаги и алиасы после каждого Tab. Разберём, что именно генерируется, куда положить скрипт для каждого шелла, зачем нужен флаг --no-descriptions и какие грабли ждут в zsh.

Пользователь набрал 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. Что произойдёт?

- Скрипт установится в ~/.bashrc автоматически
- ✅ Скрипт напечатается в stdout — установка остаётся за пользователем
- Ничего: completion работает без установки
- Откроется редактор со скриптом

Подкоманда только печатает скрипт в stdout: куда его положить, решает пользователь или пакетный менеджер. Кобра намеренно не трогает пользовательские конфиги — установка через source <(...) или файл в каталоге completions описана в самой справке команды completion.

### Что подсказывается автоматически

Стоит понимать, за что скрипт отвечает без вашего участия. Подкоманды и их алиасы — да, включая группы и порядок из урока про группы и алиасы. Флаги — да: и локальные, и персистентные, унаследованные с корня, вместе с шорткатами и текстом помощи в описаниях. Значения булевых флагов 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](/courses/cobra/lessons/23-sborka-versii/).

### Скрытая команда gen-completion

```go
var 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.

### Что ответит утилита

```go
rootCmd.CompletionOptions.DisableDefaultCmd = true

// пользователь запускает: notes completion zsh
```

**Что выведет программа?**

- Скрипт для zsh напечатается как обычно
- Скрипт напечатается, но без описаний
- ✅ Ошибка: unknown command "completion"
- Утилита запустится, но промолчит

DisableDefaultCmd полностью убирает сгенерированную команду completion: подкоманды bash, zsh, fish, powershell вместе с ней. Cobra ответит ошибкой неизвестной команды и покажет список доступных. Это путь для утилит, которые не хотят светить completion в справке или ставят дополнение только через пакеты при сборке.

### --no-descriptions и цена описаний

По умолчанию __complete возвращает варианты вместе с описаниями: notes lis<Tab> подсказывает list с подписью «Показать заметки». Красиво, но не бесплатно: описания едут отдельным полем, и в zsh с сотнями вариантов второй столбец замедляет отрисовку списка и занимает экран. Флаг --no-descriptions на любой подкоманде completion — или DisableDescriptions в CompletionOptions — выключает подписи: останутся только имена. Третий сценарий — приватные утилиты, где описания раскрывают внутренности: путь команд ни к чему чужому глазу. Отдельно держите в голове рассинхрон версий: скрипт, сгенерированный старым бинарем, может не знать новых полей протокола __complete, поэтому правило простое — дополнение устанавливается из той же версии утилиты, которой пользуется человек. Обновили CLI — попросите пользователя перезаписать файл дополнения или обновите его в пакете при сборке.

### Проверка: скрытая команда __complete

**Вопрос:** Зачем в дереве команд есть скрытая команда __complete?

- Она удаляет устаревшие скрипты дополнения
- ✅ Её вызывают функции из сгенерированного скрипта, чтобы получить варианты
- Она печатает список всех команд для справки
- Она тестирует скорость автодополнения

Сгенерированный скрипт — это обвязка: при каждом Tab он вызывает notes __complete со всеми введёнными словами, а бинарник отвечает вариантами и директивами. Поэтому дополнение автоматически знает про алиасы, группы и функции ValidArgsFunction — вся логика живёт в программе.

> **Zsh молча показывает файлы вместо команд.** Классический кейс: скрипт _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.

```go
func init() {
        rootCmd.CompletionOptions.___ = ___
        rootCmd.Execute()
}
```

Подсказка: Поле из структуры CompletionOptions: команда остаётся рабочей, но исчезает из списка справки — в отличие от DisableDefaultCmd, который удаляет её совсем.

Правильные ответы в пропусках: `HiddenDefaultCmd`, `true`

### Соберите main с настройкой completion

Расставьте: функция main, скрытие completion, вызов Execute с обработкой ошибки и выход.

Правильный порядок строк:

1. `func main() {`
2. `  rootCmd.CompletionOptions.HiddenDefaultCmd = true`
3. `  if err := rootCmd.Execute(); err != nil {`
4. `    os.Exit(1)`
5. `  }`
6. `}`

### Карточки: автодополнение

- **completion** — Сгенерированная Cobra команда с подкомандами bash, zsh, fish, powershell; каждая печатает скрипт автодополнения в stdout.
- **__complete** — Скрытая команда, которую вызывает сгенерированный скрипт: бинарник получает введённые слова и возвращает варианты дополнения.
- **CompletionOptions** — Настройки сгенерированного дополнения: DisableDefaultCmd удаляет команду, HiddenDefaultCmd прячет из справки, DisableDescriptions выключает описания.
- **--no-descriptions** — Флаг подкоманд completion: варианты без поясняющих подписей. Ускоряет zsh на больших списках и скрывает описания команд.
- **fpath и _notes** — Zsh-схема установки: скрипт кладётся в каталог из fpath под именем _<команда>, загружается через compinit — в oh-my-zsh он уже вызван.

### Что дальше

Скрипт из этого урока подсказывает команды и флаги — но не аргументы: после notes show Tab предложит файлы, а не имена заметок. Это чинит [ValidArgsFunction](/courses/cobra/lessons/18-kastomnye-completion/) — следующий урок про динамические подсказки из файлов и API. Дополнение — часть «обвязки» CLI наравне с man-страницами: их генерацию разбирает урок про [документацию](/courses/cobra/lessons/19-dokumentaciya-cli/), а упаковку скриптов в релиз — урок про [сборку](/courses/cobra/lessons/23-sborka-versii/). Обзор курса — на странице [Cobra](/courses/cobra/). Практика на закрепление: установите дополнение для своего шелла, скройте completion из справки и проверьте, что Tab подсказывает и подкоманды, и флаги --no-descriptions против обычных.

## Коротко: что запомнить

- 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.
