Типы пакета flag
Categories:
type ErrorHandling
type ErrorHandling int
ErrorHandling
определяет, как будет вести себя FlagSet.Parse
в случае неудачи разбора.
const (
ContinueOnError ErrorHandling = iota // Возвращаем описательную ошибку.
ExitOnError // Вызов os.Exit(2) или для -h/-help Exit(0).
PanicOnError // Вызываем панику с описанием ошибки.
)
Эти константы заставляют FlagSet.Parse
вести себя так, как описано в случае неудачи разбора.
type Flag
type Flag struct {
Name string // имя в командной строке
Usage string // справочное сообщение
Value Value // значение по умолчанию
DefValue string // значение по умолчанию (в виде текста); для сообщения об использовании
}
Флаг представляет состояние флага.
func Lookup
func Lookup(name string) *Flag
Lookup
возвращает структуру Flag именованного флага командной строки, возвращая nil
, если таковой не существует.
type FlagSet
type FlagSet struct {
// Usage - это функция, вызываемая при возникновении ошибки во время разбора флагов.
// Поле представляет собой функцию (не метод), которая может быть изменена для указания на
// пользовательский обработчик ошибок. То, что произойдет после вызова Usage, зависит
// от настройки ErrorHandling; для командной строки по умолчанию
// установлено значение ExitOnError, которое завершает работу программы после вызова Usage.
Usage func()
// содержит отфильтрованные или неэкспонированные поля
}
FlagSet
представляет собой набор определенных флагов. Нулевое значение FlagSet
не имеет имени и имеет обработку ошибки ContinueOnError
.
Имена флагов должны быть уникальными в пределах FlagSet
. Попытка определить флаг, имя которого уже используется, приведет к панике.
Пример
package main
import (
"flag"
"fmt"
"os"
"time"
)
func main() {
if len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
switch os.Args[1] {
case "start":
start(os.Args[2:])
case "stop":
stop(os.Args[2:])
case "help", "-h", "--help":
printUsage()
default:
fmt.Printf("error: unknown command %q\n", os.Args[1])
printUsage()
os.Exit(1)
}
}
func start(args []string) {
fs := flag.NewFlagSet("start", flag.ExitOnError)
addr := fs.String("addr", ":8080", "address to listen on")
logLevel := fs.String("log-level", "info", "log level (debug, info, warn, error)")
if err := fs.Parse(args); err != nil {
fs.Usage()
os.Exit(1)
}
fmt.Printf("Starting server on %s with log level %s\n", *addr, *logLevel)
// Здесь была бы реальная логика запуска сервера
}
func stop(args []string) {
fs := flag.NewFlagSet("stop", flag.ExitOnError)
timeout := fs.Duration("timeout", 5*time.Second, "stop timeout duration")
force := fs.Bool("force", false, "force shutdown")
if err := fs.Parse(args); err != nil {
fs.Usage()
os.Exit(1)
}
fmt.Printf("Stopping server (timeout=%v, force=%t)\n", *timeout, *force)
// Здесь была бы реальная логика остановки сервера
}
func printUsage() {
fmt.Println("Usage:")
fmt.Println(" httpd start [-addr :port] [-log-level level]")
fmt.Println(" httpd stop [-timeout duration] [-force]")
fmt.Println(" httpd help")
fmt.Println("\nFlags:")
fmt.Println(" -h, --help Show this help message")
}
Как использовать:
- Запуск сервера:
./httpd start -addr :9999 -log-level debug
- Остановка сервера:
./httpd stop -timeout 10s -force
- Просмотр справки:
./httpd help
# или
./httpd -h
var CommandLine *FlagSet
CommandLine
- это набор флагов командной строки по умолчанию, взятый из os.Args. Функции верхнего уровня, такие как BoolVar, Arg и так далее, являются обертками для методов CommandLine
.
func NewFlagSet
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet
NewFlagSet
возвращает новый пустой набор флагов с указанным именем и свойством обработки ошибок. Если имя не пустое, оно будет выведено в сообщении об использовании по умолчанию и в сообщениях об ошибках.
Пример
Объяснение flag.NewFlagSet
с примерами
Функция flag.NewFlagSet
создает новый независимый набор флагов, что особенно полезно для реализации:
- Подкоманд в CLI-приложениях (например,
git commit
,docker run
) - Изолированных групп флагов
- Повторного использования флагов в разных контекстах
Параметры:
name
- имя набора флагов (используется в сообщениях об ошибках и справке)errorHandling
- стратегия обработки ошибок:flag.ContinueOnError
- продолжить выполнение после ошибкиflag.ExitOnError
- вызватьos.Exit(2)
при ошибкеflag.PanicOnError
- вызватьpanic
при ошибке
Пример 1: Базовое использование
package main
import (
"flag"
"fmt"
)
func main() {
// Создаем новый набор флагов
fs := flag.NewFlagSet("example", flag.ExitOnError)
// Добавляем флаги
verbose := fs.Bool("verbose", false, "enable verbose output")
port := fs.Int("port", 8080, "port to listen on")
// Парсим аргументы
fs.Parse([]string{"-verbose", "-port", "9090"})
// Используем значения
fmt.Printf("Verbose: %v, Port: %d\n", *verbose, *port)
}
Пример 2: Подкоманды (как в git/docker)
package main
import (
"flag"
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Expected 'greet' or 'time' subcommands")
os.Exit(1)
}
switch os.Args[1] {
case "greet":
// Набор флагов для команды greet
greetCmd := flag.NewFlagSet("greet", flag.ExitOnError)
name := greetCmd.String("name", "World", "name to greet")
greetCmd.Parse(os.Args[2:])
fmt.Printf("Hello, %s!\n", *name)
case "time":
// Набор флагов для команды time
timeCmd := flag.NewFlagSet("time", flag.ExitOnError)
format := timeCmd.String("format", "15:04:05", "time format")
timeCmd.Parse(os.Args[2:])
fmt.Println("Current time:", time.Now().Format(*format))
default:
fmt.Printf("Unknown command %q\n", os.Args[1])
os.Exit(1)
}
}
Пример 3: Разные стратегии обработки ошибок
package main
import (
"flag"
"fmt"
)
func main() {
// ContinueOnError - продолжить после ошибки
fs1 := flag.NewFlagSet("continue", flag.ContinueOnError)
fs1.Int("port", 0, "port number")
err := fs1.Parse([]string{"-port", "invalid"})
fmt.Println("ContinueOnError:", err)
// ExitOnError - выход при ошибке
fs2 := flag.NewFlagSet("exit", flag.ExitOnError)
fs2.Int("port", 0, "port number")
// В реальном коде это вызвало бы os.Exit(2)
fmt.Print("ExitOnError: ")
fs2.Parse([]string{"-port", "invalid"})
}
Рекомендации по использованию:
- Для CLI-приложений используйте
flag.ExitOnError
- Для библиотек лучше
flag.ContinueOnError
- Имя набора флагов помогает пользователю понять контекст ошибки
- Можно создавать несколько независимых наборов флагов в одной программе
func (*FlagSet) Arg
func (f *FlagSet) Arg(i int) string
Arg
возвращает i-й аргумент. Arg(0) — это первый оставшийся аргумент после обработки флагов. Arg возвращает пустую строку, если запрошенный элемент не существует.
func (*FlagSet) Args
func (f *FlagSet) Args() []string
Args
возвращает аргументы, не являющиеся флагами.
func (*FlagSet) Bool
func (f *FlagSet) Bool(name string, value bool, usage string) *bool
Bool
определяет флаг bool с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной bool, в которой хранится значение флага.
func (*FlagSet) BoolFunc
func (f *FlagSet) BoolFunc(name, usage string, fn func(string) error)
BoolFunc
определяет флаг с указанным именем и строкой использования без требования значений. Каждый раз, когда флаг встречается, вызывается fn со значением флага. Если fn возвращает ошибку, отличную от nil, она будет рассматриваться как ошибка разбора значения флага.
func (*FlagSet) BoolVar
func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string)
BoolVar
определяет флаг bool с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную bool, в которой будет храниться значение флага.
func (*FlagSet) Duration
func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration
Duration
определяет флаг time.Duration с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной time.Duration
, в которой хранится значение флага. Флаг принимает значение, допустимое для time.ParseDuration
.
Пример
Объяснение flag.Duration
с примерами
Метод Duration
создает флаг для работы с временными интервалами (тип time.Duration
). Это особенно полезно для параметров, которые представляют временные значения (таймауты, интервалы и т.д.).
Основные параметры:
name
- имя флага (например, “timeout”)value
- значение по умолчанию (например, 5*time.Second)usage
- описание флага для справки
Пример 1: Простое использование
package main
import (
"flag"
"fmt"
"time"
)
func main() {
// Создаем флаг с продолжительностью
timeout := flag.Duration("timeout", 3*time.Second, "operation timeout duration")
// Парсим аргументы командной строки
flag.Parse()
// Используем значение
fmt.Printf("Waiting for %v before timeout...\n", *timeout)
time.Sleep(*timeout)
fmt.Println("Timeout reached!")
}
Использование:
go run main.go -timeout=2s
# или
go run main.go -timeout=500ms
Пример 2: В составе FlagSet (подкоманды)
package main
import (
"flag"
"fmt"
"time"
)
func main() {
serverCmd := flag.NewFlagSet("server", flag.ExitOnError)
// Флаги для команды server
port := serverCmd.Int("port", 8080, "port to listen on")
readTimeout := serverCmd.Duration("read-timeout", 5*time.Second, "read timeout")
writeTimeout := serverCmd.Duration("write-timeout", 10*time.Second, "write timeout")
serverCmd.Parse([]string{"-port=9000", "-read-timeout=2s", "-write-timeout=5s"})
fmt.Printf("Server config:\n")
fmt.Printf(" Port: %d\n", *port)
fmt.Printf(" Read Timeout: %v\n", *readTimeout)
fmt.Printf(" Write Timeout: %v\n", *writeTimeout)
}
Пример 3: Поддержка разных форматов времени
Флаг Duration
автоматически поддерживает все форматы, которые понимает time.ParseDuration
:
package main
import (
"flag"
"fmt"
)
func main() {
interval := flag.Duration("interval", 1*time.Minute, "check interval")
flag.Parse()
fmt.Printf("Checking every %v\n", *interval)
fmt.Printf("In nanoseconds: %d\n", interval.Nanoseconds())
}
Допустимые форматы:
- “300ms” - миллисекунды
- “1.5h” - часы с дробной частью
- “2h45m” - комбинированный формат
- “1d” - дни (расширение Go, означает 24h)
Типичные сценарии использования:
- Таймауты операций:
timeout := flag.Duration("timeout", 30*time.Second, "operation timeout")
- Интервалы повторения:
interval := flag.Duration("interval", 1*time.Hour, "check interval")
- Временные ограничения:
ttl := flag.Duration("ttl", 24*time.Hour, "time to live")
func (*FlagSet) DurationVar
func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string)
DurationVar определяет флаг time.Duration с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную time.Duration, в которой хранится значение флага. Флаг принимает значение, допустимое для time.ParseDuration.
func (*FlagSet) ErrorHandling
func (f *FlagSet) ErrorHandling() ErrorHandling
ErrorHandling
возвращает поведение обработки ошибок набора флагов.
Пример
Объяснение ErrorHandling()
с примерами
Метод ErrorHandling()
возвращает текущую стратегию обработки ошибок для набора флагов (FlagSet
). Это полезно, когда нужно проверить или изменить поведение обработки ошибок во время выполнения.
Возможные значения:
flag.ContinueOnError
- продолжить выполнение после ошибкиflag.ExitOnError
- вызватьos.Exit(2)
при ошибкеflag.PanicOnError
- вызватьpanic
при ошибке
Пример 1: Проверка текущей стратегии
package main
import (
"flag"
"fmt"
)
func main() {
// Создаем набор флагов с разными стратегиями
fs1 := flag.NewFlagSet("server", flag.ContinueOnError)
fs2 := flag.NewFlagSet("client", flag.ExitOnError)
// Проверяем стратегии обработки ошибок
fmt.Println("Server error handling:", fs1.ErrorHandling())
fmt.Println("Client error handling:", fs2.ErrorHandling())
}
Вывод:
Server error handling: 1 // ContinueOnError
Client error handling: 2 // ExitOnError
Пример 2: Динамическое изменение поведения
package main
import (
"flag"
"fmt"
"os"
)
func main() {
fs := flag.NewFlagSet("app", flag.ContinueOnError)
fs.Int("port", 8080, "port number")
// Для демонстрации - имитируем ошибку
args := []string{"-port", "invalid"}
// Парсим с текущей стратегией (ContinueOnError)
err := fs.Parse(args)
fmt.Printf("With ContinueOnError: error=%v, port=%d\n", err, fs.Lookup("port").Value)
// Меняем стратегию на ExitOnError
fs.Init(fs.Name(), flag.ExitOnError)
fmt.Println("New error handling:", fs.ErrorHandling())
// При новой стратегии парсинг вызовет os.Exit(2)
fmt.Print("With ExitOnError: ")
fs.Parse(args) // В реальности здесь будет выход
}
Пример 3: Использование в кастомной обработке ошибок
package main
import (
"flag"
"fmt"
"os"
)
func main() {
fs := flag.NewFlagSet("config", flag.ContinueOnError)
fs.String("config", "", "config file path")
fs.Duration("timeout", 5*time.Second, "operation timeout")
err := fs.Parse(os.Args[1:])
switch fs.ErrorHandling() {
case flag.ContinueOnError:
if err != nil {
fmt.Printf("Configuration error: %v\n", err)
// Продолжаем работу с значениями по умолчанию
}
case flag.ExitOnError:
// Обработка уже выполнена flag.Parse
case flag.PanicOnError:
// panic уже вызван
}
// Основная логика приложения
fmt.Println("Using config:", fs.Lookup("config").Value)
}
Основные сценарии использования:
- Библиотеки - когда нужно передать управление ошибкой вызывающему коду
- Тестирование - проверка поведения при разных стратегиях
- Гибкие CLI-утилиты - изменение поведения в зависимости от режима работы
Советы:
- Для приложений обычно используют
ExitOnError
- Для библиотек лучше
ContinueOnError
PanicOnError
редко используется на практике
func (*FlagSet) Float64
func (f *FlagSet) Float64(name string, value float64, usage string) *float64
Float64
определяет флаг float64 с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной float64, в которой хранится значение флага.
func (*FlagSet) Float64Var
func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string)
Float64Var
определяет флаг float64 с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную float64, в которой хранится значение флага.
func (*FlagSet) Func
func (f *FlagSet) Func(name, usage string, fn func(string) error)
Func
определяет флаг с указанным именем и строкой использования. Каждый раз, когда флаг встречается, вызывается fn со значением флага. Если fn возвращает ошибку, отличную от nil, она будет рассматриваться как ошибка разбора значения флага.
func (*FlagSet) Init
func (f *FlagSet) Init(name string, errorHandling ErrorHandling)
Init
устанавливает имя и свойство обработки ошибок для набора флагов. По умолчанию нулевой FlagSet использует пустое имя и политику обработки ошибок ContinueOnError.
func (*FlagSet) Int
func (f *FlagSet) Int(name string, value int, usage string) *int
Int
определяет флаг int с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной int, в которой хранится значение флага.
func (*FlagSet) Int64
func (f *FlagSet) Int64(name string, value int64, usage string) *int64
Int64
определяет флаг int64 с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной int64, в которой хранится значение флага.
func (*FlagSet) Int64Var
func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string)
Int64Var
определяет флаг int64 с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную int64, в которой хранится значение флага.
func (*FlagSet) IntVar
func (f *FlagSet) IntVar(p *int, name string, value int, usage string)
IntVar
определяет флаг int с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную int, в которой будет храниться значение флага.
func (*FlagSet) Lookup
func (f *FlagSet) Lookup(name string) *Flag
Lookup
возвращает структуру Flag с указанным именем флага, возвращая nil, если такой флаг не существует.
func (*FlagSet) NArg
func (f *FlagSet) NArg() int
NArg
— количество аргументов, оставшихся после обработки флагов.
func (*FlagSet) NFlag
func (f *FlagSet) NFlag() int
NFlag
возвращает количество установленных флагов.
func (*FlagSet) Name
func (f *FlagSet) Name() string
Name
возвращает имя набора флагов.
func (*FlagSet) Output
func (f *FlagSet) Output() io.Writer
Output
возвращает место назначения для сообщений об использовании и ошибках. os.Stderr возвращается, если выход не был установлен или был установлен в nil.
func (*FlagSet) Parse
func (f *FlagSet) Parse(arguments []string) error
Parse
анализирует определения флагов из списка аргументов, который не должен включать имя команды. Должен вызываться после того, как все флаги в FlagSet определены, и до того, как программа получит доступ к флагам. Возвращаемое значение будет ErrHelp, если -help или -h были установлены, но не определены.
func (*FlagSet) Parsed
func (f *FlagSet) Parsed() bool
Parsed
сообщает, был ли вызван f.Parse.
func (*FlagSet) PrintDefaults
func (f *FlagSet) PrintDefaults()
PrintDefaults
выводит в стандартный поток ошибок (если не настроено иначе) значения по умолчанию всех определенных флагов командной строки в наборе. Дополнительную информацию см. в документации по глобальной функции PrintDefaults.
func (*FlagSet) Set
func (f *FlagSet) Set(name, value string) error
Set
устанавливает значение флага с указанным именем.
func (*FlagSet) SetOutput
func (f *FlagSet) SetOutput(output io.Writer)
SetOutput
устанавливает место назначения для сообщений об использовании и ошибках. Если output равно nil, используется os.Stderr.
func (*FlagSet) String
func (f *FlagSet) String(name string, value string, usage string) *string
String
определяет строковый флаг с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес строковой переменной, в которой хранится значение флага.
func (*FlagSet) StringVar
func (f *FlagSet) StringVar(p *string, name string, value string, usage string)
StringVar
определяет строковый флаг с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на строковую переменную, в которой будет храниться значение флага.
func (*FlagSet) TextVar
func (f *FlagSet) TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string)
TextVar
определяет флаг с указанным именем, значением по умолчанию и строкой использования. Аргумент p должен быть указателем на переменную, которая будет хранить значение флага, и p должен реализовывать encoding.TextUnmarshaler. Если флаг используется, его значение будет передано методу UnmarshalText p. Тип значения по умолчанию должен быть таким же, как тип p.
func (*FlagSet) Uint
func (f *FlagSet) Uint(name string, value uint, usage string) *uint
Uint
определяет флаг uint с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной uint, которая хранит значение флага.
func (*FlagSet) Uint64
func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64
Uint64
определяет флаг uint64 с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной uint64, в которой хранится значение флага.
func (*FlagSet) Uint64Var
func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string)
Uint64Var
определяет флаг uint64 с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную uint64, в которой хранится значение флага.
func (*FlagSet) UintVar
func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string)
UintVar
определяет флаг uint
с указанным именем, значением по умолчанию и строкой использования. Аргумент p
указывает на переменную uint
, в которой будет храниться значение флага.
func (*FlagSet) Var
func (f *FlagSet) Var(value Value, name string, usage string)
Var
определяет флаг с указанным именем и строкой использования. Тип и значение флага представлены первым аргументом типа Value, который обычно содержит пользовательскую реализацию Value. Например, вызывающая сторона может создать флаг, который преобразует строку, разделенную запятыми, в набор строк, предоставив набору методы Value; в частности, Set разложит строку, разделенную запятыми, на набор.
func (*FlagSet) Visit
func (f *FlagSet) Visit(fn func(*Flag))
Visit
посещает флаги в лексикографическом порядке, вызывая fn для каждого из них. Он посещает только те флаги, которые были установлены.
func (*FlagSet) VisitAll
func (f *FlagSet) VisitAll(fn func(*Flag))
VisitAll
посещает флаги в лексикографическом порядке, вызывая fn для каждого из них. Он посещает все флаги, даже те, которые не были установлены.
type Getter
type Getter interface {
Value
Get() any
}
Getter
— это интерфейс, который позволяет извлекать содержимое Value. Он оборачивает интерфейс Value, а не является его частью, поскольку появился после Go 1 и его правил совместимости. Все типы Value, предоставляемые этим пакетом, удовлетворяют интерфейсу Getter, за исключением типа, используемого Func.
Пример
Объяснение интерфейса Getter
в пакете flag
Интерфейс Getter
в пакете flag
предоставляет способ получения значения флага в виде interface{}
(тип any
). Это расширение базового интерфейса Value
, добавленное для обеспечения большей гибкости при работе с флагами.
Структура интерфейса:
type Getter interface {
Value
Get() any
}
Где:
Value
- стандартный интерфейс для работы с флагами (требует методовString()
иSet()
)Get() any
- метод, возвращающий текущее значение флага в виде пустого интерфейса
Пример 1: Проверка значения флага
package main
import (
"flag"
"fmt"
"time"
)
func main() {
// Создаем несколько флагов разных типов
flag.Int("port", 8080, "server port")
flag.Duration("timeout", 5*time.Second, "operation timeout")
flag.String("name", "default", "service name")
flag.Parse()
// Получаем значения через Getter
printFlagValue("port")
printFlagValue("timeout")
printFlagValue("name")
}
func printFlagValue(name string) {
if f := flag.Lookup(name); f != nil {
if getter, ok := f.Value.(flag.Getter); ok {
fmt.Printf("%s: %v (%T)\n", name, getter.Get(), getter.Get())
} else {
fmt.Printf("%s: does not implement Getter\n", name)
}
} else {
fmt.Printf("%s: flag not found\n", name)
}
}
Пример 2: Кастомный тип с Getter
package main
import (
"flag"
"fmt"
"strings"
)
type StringSlice []string
func (s *StringSlice) String() string {
return strings.Join(*s, ",")
}
func (s *StringSlice) Set(value string) error {
*s = append(*s, value)
return nil
}
func (s *StringSlice) Get() any {
return []string(*s)
}
func main() {
var hosts StringSlice
flag.Var(&hosts, "host", "target host (can be specified multiple times)")
flag.Parse()
if g, ok := flag.Lookup("host").Value.(flag.Getter); ok {
fmt.Printf("Hosts: %v\n", g.Get())
}
}
Пример 3: Использование с разными типами флагов
package main
import (
"flag"
"fmt"
"reflect"
)
func main() {
// Разные типы флагов
intFlag := flag.Int("num", 42, "an integer")
boolFlag := flag.Bool("verbose", false, "verbose output")
stringFlag := flag.String("msg", "hello", "a message")
flag.Parse()
// Проверяем реализацию Getter для каждого флага
flags := []string{"num", "verbose", "msg"}
for _, name := range flags {
f := flag.Lookup(name)
if getter, ok := f.Value.(flag.Getter); ok {
val := getter.Get()
fmt.Printf("%s: %v (type: %v)\n", name, val, reflect.TypeOf(val))
}
}
}
Когда использовать Getter:
- Когда нужно работать с флагами разных типов единообразно
- Для создания обобщенных функций обработки флагов
- При реализации кастомных типов флагов с возможностью извлечения значения
Важные замечания:
- Почти все стандартные типы флагов реализуют
Getter
- Исключение - флаги, созданные через
flag.Func()
- Для проверки реализации используйте type assertion:
if getter, ok := flag.Value.(flag.Getter); ok { value := getter.Get() // ... }
Этот интерфейс особенно полезен при создании сложных CLI-приложений, где требуется гибкая работа с флагами разных типов.
type Value
type Value интерфейс {
String() string
Set(string) error
}
Value
— это интерфейс к динамическому значению, хранящемуся в флаге. (Значение по умолчанию представлено в виде строки.)
Если Value имеет метод IsBoolFlag() bool, возвращающий true, парсер командной строки делает -name эквивалентным -name=true, а не использует следующий аргумент командной строки.
Set вызывается один раз, в порядке командной строки, для каждого присутствующего флага. Пакет flag может вызывать метод String с нулевым значением приемника, таким как указатель nil.
Пример
package main
import (
"flag"
"fmt"
"net/url"
)
// URLValue - кастомный тип для парсинга URL в флагах
type URLValue struct {
URL *url.URL // Хранит указатель на разобранный URL
}
// String реализует интерфейс flag.Value
func (v URLValue) String() string {
if v.URL != nil {
return v.URL.String()
}
return ""
}
// Set реализует интерфейс flag.Value
func (v *URLValue) Set(s string) error {
u, err := url.Parse(s)
if err != nil {
return fmt.Errorf("failed to parse URL: %w", err)
}
*v.URL = *u // Копируем разобранный URL
return nil
}
// Get реализует интерфейс flag.Getter
func (v URLValue) Get() any {
return v.URL
}
// Глобальная переменная для хранения разобранного URL
var targetURL = &url.URL{}
func main() {
// Создаем новый набор флагов
fs := flag.NewFlagSet("URLParser", flag.ExitOnError)
// Регистрируем наш кастомный флаг
fs.Var(&URLValue{targetURL}, "url", "URL to parse (e.g. 'https://example.com/path')")
// Парсим аргументы (в реальном приложении используйте os.Args[1:])
err := fs.Parse([]string{"-url", "https://golang.org/pkg/flag/"})
if err != nil {
fmt.Printf("Error parsing flags: %v\n", err)
return
}
// Выводим разобранные компоненты URL
fmt.Printf("Parsed URL components:\n")
fmt.Printf("{scheme: %q, host: %q, path: %q}\n",
targetURL.Scheme,
targetURL.Host,
targetURL.Path)
}
Комментарии по коду:
-
Интерфейс Value:
String()
используется для вывода текущего значенияSet()
парсит строку и сохраняет значение
-
Интерфейс Getter:
- Позволяет получить значение как
interface{}
- Полезно для обобщенной обработки флагов
- Позволяет получить значение как
-
Особенности реализации:
- Используем указатель на
url.URL
для модификации - Метод
Set()
должен быть с pointer receiver - Всегда проверяем ошибки парсинга URL
- Используем указатель на
-
Использование в реальных приложениях:
// Вместо fs.Parse([]string{...}) используйте: fs.Parse(os.Args[1:])
-
Дополнительные улучшения:
- Можно добавить валидацию URL (например, требовать HTTPS)
- Можно добавить метод
IsSet()
для проверки установки значения
Пример запуска:
go run main.go -url "https://example.com/api/v1?param=value"
Вывод:
Этот код теперь представляет собой надежную реализацию кастомного флага для работы с URL в Go-приложениях.