Больше не нужно искать работу мечты — присоединяйтесь к команде Клауда

Как использовать пакет Cobra в Go

Миша Курушин
Миша Курушин
Технический писатель
04 декабря 2023 г.
1473
13 минут чтения
Средний рейтинг статьи: 5

Интерфейс командной строки (CLI) — это отдельный тип приложений, которые выполняются исключительно в терминале командной строки.

Как правило, такие программы используются для управления различными рабочими инструментами, связанными с разработкой и поддержкой сетевой инфраструктуры.

Алгоритм взаимодействия прост:

  • Пользователь вводит в терминал название CLI-приложения, имя команды, параметры и иногда дополнительные флаги
  • CLI-приложение выполняет запрошенное пользователем действие и выдает текстовый ответ обратно в терминал

CLI-приложения выглядят устаревшими из-за отсутствия какого либо графического интерфейса (GUI), однако по-прежнему считаются наиболее универсальным, быстрым и удобным способом системного администрирования.

Для создания CLI в Golang есть специальный пакет Cobra, созданный сторонними разработчикам. Он построен на основе пакета flag (парсер флагов командной строки) из стандартной библиотеки языка и предоставляет более высокий уровень абстракций.

Подробнее о пакете Cobra

Cobra — это полноценная CLI-платформа для языка Go, в состав которой входит два базовых компонента:

  • Библиотека для создания современных CLI-приложений
  • CLI-инструмент для быстрого создания приложений на основе стандартных (для Cobra) файлов-обработчиков команд

Кстати, Cobra был разработан одним из членов команды Go, Стивом Франсом (spf13), изначально для проекта Hugo — специального фреймворка для создания веб-сайтов. Спустя время Cobra стала одним из самых популярных пакетов Golang.

Возможности Cobra

Cobra предоставляет ряд довольно простых функций для создания современных интерфейсов командной строки. Помимо этого в Cobra есть высокоуровневый контроллер, помогающий организовать код разрабатываемого CLI-приложения.

Cobra реализует:

  • Иерархию обработки команд
  • Мощный анализ аргументов и флагов
  • Иерархию флагов (глобальные и локальные)
  • Проверку подкоманд
  • Совместимость со стандартом POSIX
  • Автоматическое создание справки для команд и флагов

Кстати, такие крупные проекты, как Kubernetes, Hugo или CockroachDB имеют кодовую базу на Golang и для обработки команд используют именно пакет Cobra.

CLI-команды имеют довольно стандартную схему:

{приложение} {кодкоманда} [аргументы] [--флаги и их параметры]

Например, команды в реальных проектах могут выглядеть как-то так:

kubectl get all -n kube-system

Или так:

etcdctl put first second

Архитектура Cobra

Рабочие сущности можно поделить на три типа — все они так или иначе репрезентируют структуру команд в консольном терминале:

  • Команды (Commands). Указывают на конкретные действия, которые необходимо выполнить. Впрочем, как в и любом классическом CLI-приложении.
  • Аргументы (Args). Это некоторые вещи или сущности, которые передаются в команду, после чего она работает с ними и возвращает результат.
  • Флаги (Flags). Короткие модификаторы команд (то есть конкретных действий), которые вносят определенные корректировки в выполнение работ и влияют на конечный результат работы CLI-приложения.

Немного о POSIX-совместимости

В стандарте POSIX есть соглашение о паттерне (схеме) организации аргументов и флагов, которому должны следовать CLI-приложения.

Это тот самый классический формат, с которым знакомо большинство разработчиков — многочисленные служебные программы Linux (например, «ls», «cp», «useradd») и сторонние приложения следуют именно ему.

Важно помнить, что схема команд четко формализована в стандарте и представляет собой следующий вид:

имя_приложения [-a] [-b] [-c аргумент] [-d|-e]

У каждого приложения может быть несколько версий одной и той же опции — длинная и короткая. При этом есть четкое правило, что короткая версия должна состоять только из одного символа.

1. Настройка окружения

Проверка Go

На всякий случай проверьте наличие компилятора Golang в вашей системе. Это можно сделать с помощью команды запроса версии:

go version

Если Golang действительно установлен, то в консоли появится версия Go и короткое название операционной системы.

Создание директории проекта

Далее мы создадим отдельный каталог под наш Cobra-проект:

mkdir CobraProject

После этого перейдем в него:

cd CobraProject

Golang имеет свои особенности в работе его модульной системы, необходимой для подключения пакетов. Поэтому предварительно директорию с проектом необходимо проинициализировать с помощью специальной команды:

go mod init CobraProject

После этого каталог превратится в полноценный модуль Go — в консоли появится соответствующее сообщение о создании модуля с именем CobraProject.

2. Подключение пакета Cobra

Загрузка пакета из официального репозитория

Начиная с версии Go 1.18 в Golang существует специальная команда go install, автоматически выполняющая установку удаленных модулей.

Поэтому мы воспользуемся именно ей, загрузив пакет Cobra из официального репозитория на github:

go install github.com/spf13/cobra-cli@latest

Обратите внимание, что в конце есть указатель latest — мы устанавливаем самый последний релиз.

Инициализация CLI

В терминале нам станет доступен исполняемый файл cobra-cli. С помощью него мы инициализируем проект Cobra в нашем рабочей каталоге — к этому моменту вы должны находиться уже в нем:

cobra-cli init

После этого в рабочем каталоге появятся файлы, содержащие некий стандартный код пакета Cobra с названием проекта — CobraProject.

Структура файлов будет иметь следующий вид:

CobraProject/
     cmd/
          root.go
     main.go
     go.mod
   go.sum

Файл main.go является входной точкой (entry point) в CLI-приложение. Его стандартное содержимое примерно такое:

package main

import (
"CobraProject/cmd" // путь может отличаться в зависимости от расположения рабочей директории
)

func main() {
cmd.Execute()
}

Все команды размещаются в виде отдельных файлов в каталоге /cmd. При этом файл root.go является корневым обработчиком команд — по сути это базовая команда любого консольного интерфейса.

Например, рассмотрим следующую команду:

go get URL 

Здесь go является корневой командой, которая обрабатывается root.go, get — дочерняя команда, обработчик которой размещен в отличном от root.go файле.

Сборка CLI

Для сборки CLI-приложения используется та же самая команда, что и для создания обычного двоичного файла проекта Go:

go build

Стандартно исполняемый файл появится в рабочем каталоге проекта.

Чтобы использовать собранное CLI-приложение, его также необходимо установить:

go install

После этого CLI-приложение станет доступно для вызова из терминала командной строки. Чтобы воспользоваться им достаточно написать в консоль название проекта:

CobraProject

Если все работает корректно, то в консоле появится стандартный вывод для команды без параметров. Разумеется, в дальнейшем стандартный вывод можно будет изменять — это делается в файле root.go.

3. Создание функции для команды

Каждая введенная в консоль команда вызывает соответствующую функцию Go, которая выполняет логику этой команды. При этом любые параметры и флаги, указанные в консоли, передаются в функцию.

В качестве простого примера мы реализуем небольшую функцию, которая отсчитывает время в текущем часовом поясе. Для этого мы задействуем пакет time.

После инициализации CLI в рабочем каталоге должна была появиться директория cmd. Перейдем в нее:

cd cmd

Теперь создадим файл, который будет содержать нашу функцию:

touch timefunc.go

Код внутри такой:

package cmd // указываем название нашего пакета
import "time" // импортируем стандартный пакет времени Go

func getTimeFromZone(zone string) (string, error) {
     loc, err := time.LoadLocation(zone) // узнаем текущую локацию

     // проверяем на ошибку
     if err != nil {
          return "", err // возвращаем пустой результат с данными об ошибке
}

     timeNow := time.Now().In(loc) // получаем текущее время на основе локации
     return timeNow.Format(time.RFC1123), nil // возвращаем отформатированный результат без данных об ошибке
}

Как можно видеть, функция возвращает два значения — результат и данные о возможной ошибке.

4. Добавление команды в CLI

Теперь, когда функциональная часть нашего приложения готова, мы можем «зарегистрировать» команду в CLI-приложении для доступа извне.

Для этого существует отдельная команда add:

cobra-cli add timefromzone

После этого в папке cmd появится файл timefromzone.go со стандартным кодом внутри.

Кстати, в этой же папке у вас уже расположен файл root.go отвечающий за «корневую» обработку команды — то есть команды без каких-либо параметров.

Несложно догадаться, что «обработчики» консольных команд формируются в файловой системе операционной системы в виде отдельных go-исходников.

Давайте откроем новый файл и наполним его следующим кодом:

package cmd

import (
    "fmt"
    "log"
    "github.com/spf13/cobra"
)

var timefromzoneCmd = &cobra.Command {
    Use:   "timefromzone",
    Short: "Возвращает время из заданной географической зоны",
    Long: `Команда возвращает время из заданной географической зоны. Принимает только один аргумент — зона, время которой необходимо узнать. Результат возвращается в формате стандарта RFC1123.`,
    Args: cobra.ExactArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
        timefromzone:= args[0]

    timeNow, err := getTimeFromZone(timefromzone)

    if err != nil {
        log.Fatalln("Неверная временная зона")
    }

    fmt.Println(timeNow)
},
}

func init() {
    rootCmd.AddCommand(timefromzoneCmd) // добавляем новую команду к корневой команде
}

Разберемся, что означает каждое поле:

  • Use. Название, под которым будет доступна команда из терминала
  • Short. Краткое описание команды, которое будет доступно пользователю из консоли
  • Long. Полное описание команды, которое будет доступно пользователю из консоли
  • Args. Точное количество аргументов, необходимое для работы команды
  • Run. Функция-обработчик, внутри которой мы вызываем и обрабатываем ранее созданную функцию getTimeFromZone.

На самом деле в некоторых случаях вы могли бы упростить кодовую базу, написав нужную логику прямо внутри функции-обработчика команды:

import "time"

var timefromzoneCmd = &cobra.Command {
    Use:   "timefromzone",
    Short: "Возвращает время из заданной географической зоны",
    Long: `Команда возвращает время из заданной географической зоны. Принимает только один аргумент — зона, время которой необходимо узнать. Результат возвращается в формате стандарта RFC1123.`,
    Args: cobra.ExactArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
        zone := args[0]

    loc, err := time.LoadLocation(zone)

    if err != nil {
        log.Fatalln("Временная зона указана неверно")
    }

    fmt.Println(time.Now().In(loc).Format(time.RFC1123))
},
}

Все! Команда добавлена. Остается только заново переустановить наше CLI-приложение:

go install

Теперь мы можем обратиться к нашему приложению через консоль, указав название команды и передав в качестве аргумента кодовое имя часового пояса:

CobraProject timefromzone Europe/Moscow

Консольный вывод будет примерно таким:

Fri, 10 Nov 2023 22:41:06 Europe/Moscow

Кстати, полный список существующих часовых поясов и их кодовые названия можно посмотреть на соответствующей странице в Википедии.

5. Добавление флагов в CLI

Как правило, при выполнении консольных помимо параметров можно также указывать флаги.

Флаги — это опции, которые вносят некоторые изменения в поведение конкретных команд. Флаг легко определить по предшествующему ему дефису (или двух).

Наличие флагов в CLI-приложении добавляет вариативность и гибкость в поведение команд. Без флагов пришлось бы создавать множество сложных функций с большим количеством повторяющегося кода.

В этом смысле флаги позволяют унифицировать консольное приложение. При этом в Cobra флаги можно поделить на два условных типа:

  • Локальные. Действуют только в рамках конкретной команды.
  • Постоянные. Могут применяться сразу ко всем командам и их подкомандам.

Давайте перейдем в ранее созданный файл timefromzone.go и изменим в конце функцию инициализации, добавив обработку флагов:

func init() {
    rootCmd.AddCommand(timefromzoneCmd) // добавили выше определенную команду к корневой команде
    timefromzoneCmd.Flags().String("format", "", "Выводит время в формате yyyy-mm-dd") // добавили флаг к выше определенной команде, указав информацию о нем
}

Теперь мы можем воспользоваться флагом в функции Run определенной команды. В этом случае полный код файла timefromzone.go окажется таким:

package cmd

import (
    "fmt"
    "time"
    "github.com/spf13/cobra"
)

var timefromzoneCmd = &cobra.Command {
    Use:   "timefromzone",
    Short: "Возвращает время из заданной географической зоны",
    Long: `Команда возвращает время из заданной географической зоны. Принимает только один аргумент — зона, время которой необходимо узнать. Результат возвращается в формате стандарта RFC1123.`,
    Args: cobra.ExactArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
        var date string

        zone := args[0]

        loc, _ := time.LoadLocation(zone)
        fla, _ := cmd.Flags().GetString("format")

        if fla != "" {
            date = time.Now().In(loc).Format(fla)
        } else {
            date = time.Now().In(loc).Format(time.RFC1123)
        }

        fmt.Printf("Текущее время в часовом поясе %v: %v\n", loc, date)
    },
}

func init() {
    rootCmd.AddCommand(timefromzoneCmd)
    timefromzoneCmd.Flags().String("format", "", "Выводит время в формате yyyy-mm-dd")
}

Повторно переустановим наше CLI-приложение:

go install

И выполним созданную команду, но уже с флагом:

CobraProject timefromzone Europe/Moscow --format 2006-01-02

Теперь за счет флага команда «видит» явно указанный формат даты (ГГГГ-ММ-ДД) и выводит следующий результат:

Текущее время в часовом поясе Europe/Moscow: 2023.11.10

Заключение

Пакет Cobra для языка программирования Golang — отличное решение, помогающее абстрагироваться от сложных низкоуровневых функций парсинга командной строки, которые предоставляет стандартная библиотека.

Можно сказать, Cobra — своего рода фреймворк для CLI-приложений, который снимает с разработчика «головную боль» при работе с консольным терминалом и фокусирует ресурсы преимущественно на бизнес-логике.

Каждая команда представляет собой отдельный файл в директории /cmd, который можно параметризировать с помощью флагов.

Это удобно, ведь можно в явном виде выстраивать иерархию команд и контролировать процесс их обработки за счет редактирования hook-функций, вроде init или run.

Благодаря такой особенности Cobra CLI-приложение приобретает более структурный вид и имеет меньше беспорядка, формируя определенный фреймворк.

Помните, что Cobra создана сторонними разработчиками, поэтому размещена в отдельном репозитории на GitHub и не является частью стандартной библиотеки Golang.

Также на официальном сайте Cobra можно найти инструкции по установке и использованию парсера командной строки.

04 декабря 2023 г.
1473
13 минут чтения
Средний рейтинг статьи: 5
Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
Комментарии 0