Введение
cobraЭто библиотека командной строки, которую можно использовать для написания программ командной строки. В то же время он также обеспечивает леса, Фреймворк для создания приложений на основе кобры. Многие известные проекты с открытым исходным кодом используют библиотеку cobra для построения командной строки, напримерKubernetes,Hugo,etcdИ т. д. и т. д. В этой статье представлены основы использования и некоторые интересные функции библиотеки cobra.
Об автореspf13, вот еще два слова. У spf13 много проектов с открытым исходным кодом, и качество его проектов с открытым исходным кодом относительно высокое. Я думаю, что каждый, кто использовал vim, знаетspf13-vim, известный как окончательная конфигурация vim. Его можно настроить одним щелчком мыши, что, безусловно, является благом для ленивых людей, таких как я. егоviperпредставляет собой полное решение для конфигурации. Он отлично поддерживает файлы конфигурации свойств JSON/TOML/YAML/HCL/envfile/Java и другие форматы, а также некоторые более практичные функции, такие как горячее обновление конфигурации, несколько каталогов поиска, сохранение конфигурации и т. д. Есть также очень популярные генераторы статических сайтов.hugoТакже его работа.
быстрый в использовании
Все сторонние библиотеки необходимо сначала установить, а затем использовать. Установлена следующая командаcobra
Программа-генератор и библиотека cobra:
$ go get github.com/spf13/cobra/cobra
если естьgolang.org/x/text
Для таких ошибок, как библиотека не найдена, вам необходимо вручную загрузить библиотеку с GitHub, а затем выполнить указанную выше команду установки. Я написал блог раньшеНастройка среды разработки Goупомянул этот метод.
Мы реализуем простую программу командной строки git, конечно, это не настоящий git, просто имитирует его командную строку. наконец прошлоos/exec
Библиотека вызывает внешнюю программу для выполнения настоящей команды git и возвращает результат.
Итак, нам нужно установить git в нашу систему, а git находится в пути к исполняемому файлу. В настоящее время мы добавляем только одну подкомандуversion
. Структура каталогов следующая:
▾ get-started/
▾ cmd/
helper.go
root.go
version.go
main.go
root.go
:
package cmd
import (
"errors"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command {
Use: "git",
Short: "Git is a distributed version control system.",
Long: `Git is a free and open source distributed version control system
designed to handle everything from small to very large projects
with speed and efficiency.`,
Run: func(cmd *cobra.Command, args []string) {
Error(cmd, args, errors.New("unrecognized command"))
},
}
func Execute() {
rootCmd.Execute()
}
version.go
:
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command {
Use: "version",
Short: "version subcommand show git version info.",
Run: func(cmd *cobra.Command, args []string) {
output, err := ExecuteCommand("git", "version", args...)
if err != nil {
Error(cmd, args, err)
}
fmt.Fprint(os.Stdout, output)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
main.go
Файл просто вызывает запись команды:
package main
import (
"github.com/darjun/go-daily-lib/cobra/get-started/cmd"
)
func main() {
cmd.Execute()
}
Для удобства кодирования вhelpers.go
Вызов внешних программ и функций обработки ошибок инкапсулирован в:
package cmd
import (
"fmt"
"os"
"os/exec"
"github.com/spf13/cobra"
)
func ExecuteCommand(name string, subname string, args ...string) (string, error) {
args = append([]string{subname}, args...)
cmd := exec.Command(name, args...)
bytes, err := cmd.CombinedOutput()
return string(bytes), err
}
func Error(cmd *cobra.Command, args []string, err error) {
fmt.Fprintf(os.Stderr, "execute %s args:%v error:%v\n", cmd.Name(), args, err)
os.Exit(1)
}
Каждая программа кобры имеет корневую команду, к которой можно добавить любое количество подкоманд. мы вversion.go
изinit
для добавления подкоманд к корневой команде.
Скомпилируйте программу. Обратите внимание, что вы не можете напрямуюgo run main.go
, которая больше не является однофайловой программой. Если вы вынуждены использовать его, пожалуйста, используйтеgo run .
:
$ go build -o main.exe
Справочная информация автоматически генерируется коброй, очень круто:
$ ./main.exe -h
Git is a free and open source distributed version control system
designed to handle everything from small to very large projects
with speed and efficiency.
Usage:
git [flags]
git [command]
Available Commands:
help Help about any command
version version subcommand show git version info.
Flags:
-h, --help help for git
Use "git [command] --help" for more information about a command.
Справка для отдельных подкоманд:
$ ./main.exe version -h
version subcommand show git version info.
Usage:
git version [flags]
Flags:
-h, --help help for version
Вызвать подкоманду:
$ ./main.exe version
git version 2.19.1.windows.1
Неизвестная подкоманда:
$ ./main.exe clone
Error: unknown command "clone" for "git"
Run 'git --help' for usage.
При компиляции можноmain.exe
изменить наgit
, будет удобнее пользоваться 🤩.
$ go build -o git
$ ./git version
git version 2.19.1.windows.1
При использовании кобры для построения командной строки структура каталогов программы, как правило, относительно проста.Рекомендуется следующая структура:
▾ appName/
▾ cmd/
cmd1.go
cmd2.go
cmd3.go
root.go
main.go
Каждая команда реализует файл, и все командные файлы хранятся вcmd
Под содержанием. внешнийmain.go
Инициализируйте только кобру.
характеристика
cobra предоставляет очень богатые возможности:
- Легко поддерживает подкоманды, такие как
app server
,app fetch
Ждать; - Полностью совместим с опциями POSIX (включая короткие и длинные опции);
- вложенные подкоманды;
- Варианты глобального, локального уровня. Вы можете установить параметры в нескольких местах и использовать их в определенном порядке;
- С легкостью создавайте процедурные скелеты и команды с помощью скаффолдинга.
Во-первых, необходимо уточнить три основных понятия:
- Команда: это операция, которую необходимо выполнить;
- Параметр (Arg): Параметр команды, то есть объект, которым нужно управлять;
- Параметры (флаг): параметры команды могут регулировать поведение команды.
В приведенном ниже примереserver
является (под)командой,--port
это вариант:
hugo server --port=1313
В приведенном ниже примереclone
является (под)командой,URL
параметр,--bare
это вариант:
git clone URL --bare
Заказ
В кобре как команды, так и подкоманды используютCommand
представлена структурой.Command
Есть много полей для настройки поведения команды.
На практике чаще всего используются лишь некоторые из них. Мы видели в предыдущем примереUse/Short/Long/Run
.
Use
Указывает информацию об использовании, то есть способ вызова команды в форматеname arg1 [arg2]
.name
имя команды, за которым следуетarg1
обязательный параметр,arg3
Для необязательных параметров может быть несколько параметров.
Short/Long
Обе являются справочной информацией для указанной команды, но первая краткая, а вторая подробная.
Run
это функция, которая фактически выполняет операцию.
Определить новую подкоманду так же просто, как создатьcobra.Command
переменная, установите некоторые поля, затем добавьте в корневую команду. Например, мы хотим добавитьclone
Подкоманда:
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var cloneCmd = &cobra.Command {
Use: "clone url [destination]",
Short: "Clone a repository into a new directory",
Run: func(cmd *cobra.Command, args []string) {
output, err := ExecuteCommand("git", "clone", args...)
if err != nil {
Error(cmd, args, err)
}
fmt.Fprintf(os.Stdout, output)
},
}
func init() {
rootCmd.AddCommand(cloneCmd)
}
вUse
полеclone url [destination]
Указывает, что имя подкомандыclone
,параметрurl
требуется, целевой путьdestination
По желанию.
Собираем программу какmygit
исполняемый файл, затем поместите его в$GOPATH/bin
середина. Я люблю$GOPATH/bin
ставить$PATH
, так что вы можете напрямую позвонитьmygit
команда:
$ go build -o mygit
$ mv mygit $GOPATH/bin
$ mygit clone https://github.com/darjun/leetcode
Cloning into 'leetcode'...
Вы можете продолжить добавлять команды. Но я просто поленился и перенаправил операции на настоящий git для выполнения. Это действительно не имеет практического значения. С этой идеей представьте, что мы можем объединить несколько команд для реализации многих полезных инструментов, таких как инструменты упаковки 😉.
опции
В кобре есть два варианта, одинпостоянный вариант, можно использовать команду, которая его определяет, и ее подкоманды. Определите глобальные параметры, добавив параметр в корневую команду. Другойместные варианты, может использоваться только в команде, в которой он определен.
использование кобрыpflagРазобрать параметры командной строки.pflag
Используйте в основном то же самоеflag
Так же, в этой серии статей есть введениеflag
библиотека,Флаг ежедневной библиотеки Go.
иflag
Точно так же переменные, в которых хранятся параметры, также должны быть определены заранее:
var Verbose bool
var Source string
Установите постоянные параметры:
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
Установите локальные параметры:
localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
Оба параметра одинаковы, длинное/короткое имя параметра, значение по умолчанию и справочная информация.
Ниже мы используем пример, чтобы продемонстрировать использование опций.
Предположим, мы хотим сделать простой калькулятор, поддерживающий сложение, вычитание, умножение и деление. И вы можете указать, следует ли игнорировать нечисловые параметры через параметры, и указать, следует ли сообщать об ошибке при делении на 0. Очевидно, что первую опцию следует поместить в глобальные опции, а вторую — в команду дивизии. Структура программы следующая:
▾ math/
▾ cmd/
add.go
divide.go
minus.go
multiply.go
root.go
main.go
Показать здесьdivide.go
иroot.go
, другие командные файлы аналогичны. Я вставил полный кодGitHubвверх.
divide.go
:
var (
dividedByZeroHanding int // 除 0 如何处理
)
var divideCmd = &cobra.Command {
Use: "divide",
Short: "Divide subcommand divide all passed args.",
Run: func(cmd *cobra.Command, args []string) {
values := ConvertArgsToFloat64Slice(args, ErrorHandling(parseHandling))
result := calc(values, DIVIDE)
fmt.Printf("%s = %.2f\n", strings.Join(args, "/"), result)
},
}
func init() {
divideCmd.Flags().IntVarP(÷dByZeroHanding, "divide_by_zero", "d", int(PanicOnDividedByZero), "do what when divided by zero")
rootCmd.AddCommand(divideCmd)
}
root.go
:
var (
parseHandling int
)
var rootCmd = &cobra.Command {
Use: "math",
Short: "Math calc the accumulative result.",
Run: func(cmd *cobra.Command, args []string) {
Error(cmd, args, errors.New("unrecognized subcommand"))
},
}
func init() {
rootCmd.PersistentFlags().IntVarP(&parseHandling, "parse_error", "p", int(ContinueOnParseError), "do what when parse arg error")
}
func Execute() {
rootCmd.Execute()
}
существуетdivide.go
Варианты того, как обрабатывать ошибки деления на 0, определены вroot.go
Параметры того, как обрабатывать ошибки синтаксического анализа, определены в . Варианты перечислены следующим образом:
const (
ContinueOnParseError ErrorHandling = 1 // 解析错误尝试继续处理
ExitOnParseError ErrorHandling = 2 // 解析错误程序停止
PanicOnParseError ErrorHandling = 3 // 解析错误 panic
ReturnOnDividedByZero ErrorHandling = 4 // 除0返回
PanicOnDividedByZero ErrorHandling = 5 // 除0 painc
)
На самом деле логика выполнения команды не сложная, это преобразование параметров вfloat64
. Затем выполните соответствующую операцию и выведите результат.
тестовая программа:
$ go build -o math
$ ./math add 1 2 3 4
1+2+3+4 = 10.00
$ ./math minus 1 2 3 4
1-2-3-4 = -8.00
$ ./math multiply 1 2 3 4
1*2*3*4 = 24.00
$ ./math divide 1 2 3 4
1/2/3/4 = 0.04
По умолчанию ошибки синтаксического анализа игнорируются, и оценивается только результат правильно сформированных аргументов:
$ ./math add 1 2a 3b 4
1+2a+3b+4 = 5.00
$ ./math divide 1 2a 3b 4
1/2a/3b/4 = 0.25
Установить обработку сбоя парсинга, 2 означает выход из программы, 3 означает панику (см. перечисление выше):
$ ./math add 1 2a 3b 4 -p 2
invalid number: 2a
$ ./math add 1 2a 3b 4 -p 3
panic: strconv.ParseFloat: parsing "2a": invalid syntax
goroutine 1 [running]:
github.com/darjun/go-daily-lib/cobra/math/cmd.ConvertArgsToFloat64Slice(0xc00004e300, 0x4, 0x6, 0x3, 0xc00008bd70, 0x504f6b, 0xc000098600)
D:/code/golang/src/github.com/darjun/go-daily-lib/cobra/math/cmd/helper.go:58 +0x2c3
github.com/darjun/go-daily-lib/cobra/math/cmd.glob..func1(0x74c620, 0xc00004e300, 0x4, 0x6)
D:/code/golang/src/github.com/darjun/go-daily-lib/cobra/math/cmd/add.go:14 +0x6d
github.com/spf13/cobra.(*Command).execute(0x74c620, 0xc00004e1e0, 0x6, 0x6, 0x74c620, 0xc00004e1e0)
D:/code/golang/src/github.com/spf13/cobra/command.go:835 +0x2b1
github.com/spf13/cobra.(*Command).ExecuteC(0x74d020, 0x0, 0x599ee0, 0xc000056058)
D:/code/golang/src/github.com/spf13/cobra/command.go:919 +0x302
github.com/spf13/cobra.(*Command).Execute(...)
D:/code/golang/src/github.com/spf13/cobra/command.go:869
github.com/darjun/go-daily-lib/cobra/math/cmd.Execute(...)
D:/code/golang/src/github.com/darjun/go-daily-lib/cobra/math/cmd/root.go:45
main.main()
D:/code/golang/src/github.com/darjun/go-daily-lib/cobra/math/main.go:8 +0x35
Что касается варианта деления на 0, попробуйте сами.
Внимательные друзья должны были заметить, что в программе все же есть некоторые несовершенства. Например, здесь, если вы введете нечисловой параметр, этот параметр также будет показан в результате:
$ ./math add 1 2 3d cc
1+2+3d+cc = 3.00
Если вам интересно, вы можете улучшить его самостоятельно~
строительные леса
В предыдущем введении мы также видели, что структура команды кобры относительно фиксирована. Вот тут-то и появляются инструменты, которые могут значительно повысить эффективность нашей разработки.
Программа скаффолдинга также была установлена, когда ранее была установлена библиотека cobra. Ниже мы опишем, как использовать этот генератор.
использоватьcobra init
Команда создает приложение Cobra:
$ cobra init scaffold --pkg-name github.com/darjun/go-daily-lib/cobra/scaffold
вscaffold
имя приложения, за которым следуетpkg-name
option указывает путь к пакету. Сгенерированная структура каталогов программы выглядит следующим образом:
▾ scaffold/
▾ cmd/
root.go
LICENSE
main.go
Эта структура проекта точно такая же, как и предыдущая, а также структура, рекомендованная Cobra. Так же,main.go
Только вход.
существуетroot.go
, инструмент дополнительно генерирует для нас некоторый код.
В команду root добавлен параметр файла конфигурации, необходимый для большинства приложений:
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.scaffold.yaml)")
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
В обратном вызове завершения инициализации, если параметр оказывается пустым, по умолчанию используется домашний каталог..scaffold.yaml
документ:
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
viper.AddConfigPath(home)
viper.SetConfigName(".scaffold")
}
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
Вот что я представил несколько дней назадgo-homedirбиблиотека. Файл конфигурации читается с использованием собственного проекта spf13 с открытым исходным кодом.viper(Ядовитый дракон? Какой гений называть имена).
В дополнение к файлам кода, Cobra также создает файл LICENSE.
Сейчас эта программа ничего не умеет, нам нужно добавить к ней подкоманды, используяcobra add
Заказ:
$ cobra add date
Команда находится вcmd
добавлено в каталогdate.go
документ. Базовая структура настроена, осталось только изменить некоторые описания и добавить некоторые параметры.
Теперь мы реализуем такую функцию для печати календаря этого месяца в соответствии с входящим годом и месяцем. Если параметры не переданы, используются текущий год и месяц.
Определения опций:
func init() {
rootCmd.AddCommand(dateCmd)
dateCmd.PersistentFlags().IntVarP(&year, "year", "y", 0, "year to show (should in [1000, 9999]")
dateCmd.PersistentFlags().IntVarP(&month, "month", "m", 0, "month to show (should in [1, 12]")
}
ИсправлятьdateCmd
изRun
функция:
Run: func(cmd *cobra.Command, args []string) {
if year < 1000 && year > 9999 {
fmt.Fprintln(os.Stderr, "invalid year should in [1000, 9999], actual:%d", year)
os.Exit(1)
}
if month < 1 && year > 12 {
fmt.Fprintln(os.Stderr, "invalid month should in [1, 12], actual:%d", month)
os.Exit(1)
}
showCalendar()
}
showCalendar
функция заключается в использованииtime
Предоставленный метод реализован и здесь повторяться не будет. Если вам интересно, вы можете зайти на мой GitHub, чтобы увидеть реализацию.
Посмотрите, как работает программа:
$ go build -o main.exe
$ ./main.exe date
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
$ ./main.exe date --year 2019 --month 12
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Вы можете добавить другие функции в эту программу, попробуйте~
разное
Cobra предоставляет очень богатые функции и настраиваемые интерфейсы, такие как:
- Настройте функции ловушек для выполнения определенных операций до и после выполнения команды;
- Создание документов в формате Markdown/ReStructed Text/Man Page;
- И т. д. и т. д.
Из-за нехватки места они не будут представлены по одному. Заинтересованные могут провести собственное исследование. Библиотека cobra широко используется, и используется многими известными проектами, о которых также упоминалось ранее. Узнайте, как эти проекты используют Cobra, чтобы узнать о возможностях Cobra и рекомендациях. Это также отличный способ узнать о проектах с открытым исходным кодом.
Весь пример кода в этой статье был загружен на мой GitHub,Ежедневная библиотека,GitHub.com/Darenjun/go-of…
Ссылаться на
- cobraРепозиторий GitHub
я
Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~
Эта статья опубликована в блогеOpenWriteвыпуск!