Это многостраничный печатный вид этого раздела. Нажмите что бы печатать.

Вернуться к обычному просмотру страницы.

Описание пакета flag языка программирования Go

Пакет flag реализует разбор флагов командной строки.

Использование

Определите флаги с помощью flag.String, Bool, Int и т. д.

Это объявляет целочисленный флаг -n, хранящийся в указателе nFlag, с типом *int:

import «flag»
var nFlag = flag.Int("n", 1234, "сообщение справки для флага n")

При желании можно привязать флаг к переменной с помощью функций Var().

var flagvar int
func init() {
    flag.IntVar(&flagvar, "flagname", 1234, "сообщение справки для flagname")
}

Или можно создать пользовательские флаги, которые удовлетворяют интерфейсу Value (с указателями-приемниками), и связать их с разбором флагов с помощью

flag.Var(&flagVal, "name", "сообщение справки для flagname")

Для таких флагов значением по умолчанию является просто начальное значение переменной.

После определения всех флагов вызовите

flag.Parse()

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

Затем флаги можно использовать напрямую. Если вы используете сами флаги, все они являются указателями; если вы связываете их с переменными, они являются значениями.

fmt.Println(«ip имеет значение », *ip)
fmt.Println(«flagvar имеет значение », flagvar)

После разбора аргументы, следующие за флагами, доступны как срез flag.Args или по отдельности как flag.Arg(i). Аргументы индексируются от 0 до flag.NArg-1.

Синтаксис флагов командной строки ¶

Допускаются следующие формы:

-flag
--flag   // также допускаются двойные тире
-flag=x
-flag x  // только небулевые флаги

Можно использовать один или два тире; они эквивалентны. Последняя форма не допускается для булевых флагов, поскольку значение команды

cmd -x *

где * — подстановочный знак оболочки Unix, изменится, если есть файл с именем 0, false и т. д. Для отключения булевого флага необходимо использовать форму -flag=false.

Разбор флагов прекращается непосредственно перед первым аргументом, не являющимся флагом («-» является аргументом, не являющимся флагом), или после терминатора «–».

  • Целочисленные флаги принимают значения 1234, 0664, 0x1234 и могут быть отрицательными.
  • Булевы флаги могут быть: 1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False

Флаги продолжительности принимают любой ввод, допустимый для time.ParseDuration.

Набор флагов командной строки по умолчанию управляется функциями верхнего уровня. Тип FlagSet позволяет определять независимые наборы флагов, например для реализации подкоманд в интерфейсе командной строки. Методы FlagSet аналогичны функциям верхнего уровня для набора флагов командной строки.

Пример
package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strings"
	"time"
)

// Пример 1: Флаг строки с именем "species" и значением по умолчанию "gopher"
var species = flag.String("species", "gopher", "the species we are studying")

// Пример 2: Два флага (длинный и короткий) с общей переменной
var gopherType string

func init() {
	const (
		defaultGopher = "pocket"
		usage         = "the variety of gopher"
	)
	flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
	flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
}

// Пример 3: Пользовательский тип флага - срез интервалов времени
type interval []time.Duration

// String реализует интерфейс flag.Value
func (i *interval) String() string {
	if len(*i) == 0 {
		return ""
	}
	var b strings.Builder
	for idx, d := range *i {
		if idx > 0 {
			b.WriteString(", ")
		}
		b.WriteString(d.String())
	}
	return b.String()
}

// Set реализует интерфейс flag.Value
func (i *interval) Set(value string) error {
	// Разрешаем множественные установки флага для накопления значений
	for _, dt := range strings.Split(value, ",") {
		duration, err := time.ParseDuration(strings.TrimSpace(dt))
		if err != nil {
			return fmt.Errorf("invalid duration %q: %v", dt, err)
		}
		*i = append(*i, duration)
	}
	return nil
}

var intervalFlag interval

func init() {
	flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
}

func main() {
	// Парсим флаги
	flag.Parse()

	// Выводим значения флагов
	fmt.Println("Species:", *species)
	fmt.Println("Gopher type:", gopherType)
	fmt.Println("Intervals:", intervalFlag.String())

	// Пример использования
	if len(intervalFlag) == 0 {
		fmt.Println("No intervals specified. Use -deltaT flag")
		os.Exit(1)
	}

	// Демонстрация работы с интервалами
	for i, d := range intervalFlag {
		fmt.Printf("Event %d will happen after %v\n", i+1, d)
		time.Sleep(d) // В реальном коде здесь была бы полезная работа
	}
}

Как использовать:

  1. Сборка:
go build -o app
  1. Примеры запуска:
# Простой запуск
./app -species=rabbit -gopher_type=arctic -deltaT=1s,2s,3s

# Использование короткого флага
./app -g=arctic -deltaT=500ms,1s

# Просмотр помощи
./app -help

Переменные

var ErrHelp = errors.New("flag: help requested")

ErrHelp - это ошибка, возвращаемая при вызове флага -help или -h, но такой флаг не определен.

var Usage = func() {
fmt.Fprintf(CommandLine.Output(), "Usage of %s:\n", os.Args[0])
PrintDefaults()
}

Usage печатает сообщение об использовании всех определенных флагов командной строки в выходной файл CommandLine, который по умолчанию является os.Stderr.

Функция вызывается при возникновении ошибки во время разбора флагов.

Функция - это переменная, которая может быть изменена для указания на пользовательскую функцию. По умолчанию она печатает простой заголовок и вызывает PrintDefaults; подробности о формате вывода и о том, как им управлять, см. в документации к PrintDefaults.

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

1 - Функции пакета flag

Основные функции пакета flag

func Arg

func Arg(i int) string

Arg возвращает i-й аргумент командной строки. Arg(0) — это первый аргумент, оставшийся после обработки флагов. Arg возвращает пустую строку, если запрошенный элемент не существует.

func Args

func Args() []string

Args возвращает аргументы командной строки, не являющиеся флагами.

func Bool

func Bool(name string, value bool, usage string) *bool

Bool определяет флаг bool с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной bool, в которой хранится значение флага.

func BoolFunc

добавлено в go1.21.0

func BoolFunc(name, usage string, fn func(string) error)

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

Пример
package main

import (
	"flag"
	"fmt"
	"os"
)

func main() {
	// Создаем новый набор флагов
	fs := flag.NewFlagSet("ExampleBoolFunc", flag.ContinueOnError)
	fs.SetOutput(os.Stdout) // Выводим ошибки в stdout

	// Регистрируем BoolFunc флаг
	fs.BoolFunc("log", "logs a dummy message (default: false)", func(s string) error {
		if s == "" {
			// Без значения - просто флаг присутствует
			fmt.Println("dummy message: flag is set")
			return nil
		}

		// С значением - выводим его
		fmt.Printf("dummy message: %s\n", s)
		return nil
	})

	// Пример 1: Флаг без значения
	fmt.Println("\nCase 1: -log (no value)")
	if err := fs.Parse([]string{"-log"}); err != nil {
		fmt.Println("Error:", err)
	}

	// Пример 2: Флаг с произвольным значением
	fmt.Println("\nCase 2: -log=0")
	if err := fs.Parse([]string{"-log=0"}); err != nil {
		fmt.Println("Error:", err)
	}

	// Пример 3: Неизвестный флаг
	fmt.Println("\nCase 3: -unknown")
	if err := fs.Parse([]string{"-unknown"}); err != nil {
		fmt.Println("Error:", err)
	}

	// Пример 4: Вывод помощи
	fmt.Println("\nCase 4: -help")
	fs.PrintDefaults()
}

Запуск программы:

go run main.go -log -log=hello

Результат:

Case 1: -log (no value)
dummy message: flag is set

Case 2: -log=0
dummy message: 0

Case 3: -unknown
Error: flag provided but not defined: -unknown

Case 4: -help
  -log
        logs a dummy message (default: false)

func BoolVar

func BoolVar(p *bool, name string, value bool, usage string)

BoolVar определяет флаг bool с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную bool, в которой будет храниться значение флага.

func Duration

func Duration(name string, value time.Duration, usage string) *time.Duration

Duration определяет флаг time.Duration с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной time.Duration, в которой хранится значение флага. Флаг принимает значение, допустимое для time.ParseDuration.

func DurationVar

func DurationVar(p *time.Duration, name string, value time.Duration, usage string)

DurationVar определяет флаг time.Duration с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную time.Duration, в которой хранится значение флага. Флаг принимает значение, допустимое для time.ParseDuration.

func Float64

func Float64(name string, value float64, usage string) *float64

Float64 определяет флаг float64 с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной float64, в которой хранится значение флага.

func Float64Var

func Float64Var(p *float64, name string, value float64, usage string)

Float64Var определяет флаг float64 с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную float64, в которой хранится значение флага.

func Float64Var

func Float64Var(p *float64, name string, value float64, usage string)

Float64Var определяет флаг float64 с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную float64, в которой будет храниться значение флага.

func Func

func Func(name, usage string, fn func(string) error)

Func определяет флаг с указанным именем и строкой использования. Каждый раз, когда флаг встречается, вызывается функция fn со значением флага. Если fn возвращает ошибку, отличную от nil, это будет расценено как ошибка разбора значения флага.

Пример
package main

import (
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
)

func main() {
	// Создаем новый набор флагов
	fs := flag.NewFlagSet("ExampleFunc", flag.ContinueOnError)
	fs.SetOutput(os.Stdout)

	// Переменная для хранения IP
	var ip net.IP

	// Регистрируем функцию-парсер для IP
	fs.Func("ip", "`IP address` to parse (e.g. 192.168.1.1 or ::1)", func(s string) error {
		parsedIP := net.ParseIP(s)
		if parsedIP == nil {
			return fmt.Errorf("invalid IP address format: %q", s)
		}
		ip = parsedIP
		return nil
	})

	// Тест 1: Валидный IPv4 адрес
	fmt.Println("Test case 1: Valid IPv4 address")
	if err := fs.Parse([]string{"-ip", "127.0.0.1"}); err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Parsed IP: %v\n", ip)
		fmt.Printf("Is loopback: %t\n\n", ip.IsLoopback())
	}

	// Тест 2: Невалидный IP адрес
	fmt.Println("Test case 2: Invalid IP address")
	if err := fs.Parse([]string{"-ip", "256.0.0.1"}); err != nil {
		fmt.Printf("Error: %v\n\n", err)
	} else {
		fmt.Printf("Parsed IP: %v\n", ip)
	}

	// Тест 3: Валидный IPv6 адрес
	fmt.Println("Test case 3: Valid IPv6 address")
	if err := fs.Parse([]string{"-ip", "::1"}); err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Parsed IP: %v\n", ip)
		fmt.Printf("Is loopback: %t\n\n", ip.IsLoopback())
	}

	// Вывод справки
	fmt.Println("Help message:")
	fs.PrintDefaults()
}

Вывод программы:

Test case 1: Valid IPv4 address
Parsed IP: 127.0.0.1
Is loopback: true

Test case 2: Invalid IP address
Error: invalid IP address format: "256.0.0.1"

Test case 3: Valid IPv6 address
Parsed IP: ::1
Is loopback: true

Help message:
  -ip IP address
        `IP address` to parse (e.g. 192.168.1.1 or ::1)

func Int

func Int(name string, value int, usage string) *int

Int определяет флаг int с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной int, в которой хранится значение флага.

func Int64

func Int64(name string, value int64, usage string) *int64

Int64 определяет флаг int64 с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной int64, в которой хранится значение флага.

func Int64Var

func Int64Var(p *int64, name string, value int64, usage string)

Int64Var определяет флаг int64 с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную int64, в которой хранится значение флага.

func IntVar

func IntVar(p *int, name string, value int, usage string)

IntVar определяет флаг int с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную int, в которой будет храниться значение флага.

func NArg

func NArg() int

NArg — количество аргументов, оставшихся после обработки флагов.

func NFlag

func NFlag() int

NFlag возвращает количество установленных флагов командной строки.

func Parse

func Parse()

Parse анализирует флаги командной строки из os.Args[1:]. Должен вызываться после определения всех флагов и до доступа к флагам из программы.

func Parsed

func Parsed() bool

Parsed сообщает, были ли проанализированы флаги командной строки.

func PrintDefaults

func PrintDefaults()

PrintDefaults выводит в стандартный поток ошибок (если не настроено иначе) сообщение об использовании, показывающее настройки по умолчанию всех определенных флагов командной строки. Для флага x с целочисленным значением вывод по умолчанию имеет вид

-x int
    usage-message-for-x (default 7)

Сообщение об использовании будет отображаться в отдельной строке для всех флагов, кроме флагов типа bool с однобайтовым именем.

Для флагов типа bool тип опускается, и если имя флага состоит из одного байта, сообщение об использовании отображается в той же строке.

Значение по умолчанию в скобках опускается, если значение по умолчанию для типа равно нулю.

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

flag.String("I", "", "search `directory` for include files")

вывод будет следующим

-I directory
	search directory for include files.

Чтобы изменить место назначения для сообщений флага, вызовите CommandLine.SetOutput.

func Set

func Set(name, value string) error

Set устанавливает значение флага командной строки с указанным именем.

func String

func String(name string, value string, usage string) *string

String определяет строковый флаг с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес строковой переменной, в которой хранится значение флага.

func StringVar

func StringVar(p *string, name string, value string, usage string)

StringVar определяет строковый флаг с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на строковую переменную, в которой хранится значение флага.

func TextVar

func TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string)

TextVar определяет флаг с указанным именем, значением по умолчанию и строкой использования. Аргумент p должен быть указателем на переменную, которая будет хранить значение флага, и p должен реализовывать encoding.TextUnmarshaler. Если флаг используется, его значение будет передано методу UnmarshalText p. Тип значения по умолчанию должен быть таким же, как тип p.

Пример
package main

import (
	"flag"
	"fmt"
	"net"
	"os"
)

func main() {
	// Создаем новый набор флагов
	fs := flag.NewFlagSet("ExampleTextVar", flag.ContinueOnError)
	fs.SetOutput(os.Stdout)

	// Переменная для хранения IP с значением по умолчанию
	var ip net.IP = net.IPv4(192, 168, 0, 100)

	// Регистрируем TextVar флаг для парсинга IP
	fs.TextVar(&ip, "ip", ip, "`IP address` to parse (e.g. 192.168.1.1 or ::1)")

	// Тест 1: Валидный IPv4 адрес
	fmt.Println("Test case 1: Valid IPv4 address (127.0.0.1)")
	if err := fs.Parse([]string{"-ip", "127.0.0.1"}); err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Parsed IP: %v\n", ip)
		fmt.Printf("Is loopback: %t\n\n", ip.IsLoopback())
	}

	// Тест 2: Невалидный IP адрес
	fmt.Println("Test case 2: Invalid IP address (256.0.0.1)")
	ip = nil // Сбрасываем предыдущее значение
	if err := fs.Parse([]string{"-ip", "256.0.0.1"}); err != nil {
		fmt.Printf("Error: %v\n\n", err)
	} else {
		fmt.Printf("Parsed IP: %v\n\n", ip)
	}

	// Тест 3: Вывод справки
	fmt.Println("Test case 3: Help message")
	fs.PrintDefaults()
}

Вывод программы:

Test case 1: Valid IPv4 address (127.0.0.1)
Parsed IP: 127.0.0.1
Is loopback: true

Test case 2: Invalid IP address (256.0.0.1)
Error: invalid IP address: 256.0.0.1

Test case 3: Help message
  -ip IP address
        `IP address` to parse (e.g. 192.168.1.1 or ::1) (default 192.168.0.100)

func Uint

func Uint(name string, value uint, usage string) *uint

Uint определяет флаг uint с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной uint, которая хранит значение флага.

func Uint64

func Uint64(name string, value uint64, usage string) *uint64

Uint64 определяет флаг uint64 с указанным именем, значением по умолчанию и строкой использования. Возвращаемое значение — адрес переменной uint64, в которой хранится значение флага.

func Uint64Var

func Uint64Var(p *uint64, name string, value uint64, usage string)

Uint64Var определяет флаг uint64 с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную uint64, в которой будет храниться значение флага.

func UintVar

func UintVar(p *uint, name string, value uint, usage string)

UintVar определяет флаг uint с указанным именем, значением по умолчанию и строкой использования. Аргумент p указывает на переменную uint, в которой будет храниться значение флага.

func UnquoteUsage

func UnquoteUsage(flag *Flag) (name string, usage string)

UnquoteUsage извлекает имя в обратных кавычках из строки использования для флага и возвращает его и использование без кавычек. При заданном “a `name` to show” возвращает (“name”, “a name to show”). Если обратные кавычки отсутствуют, имя является обоснованным предположением о типе значения флага или пустой строкой, если флаг является булевым.

func Var

func Var(value Value, name string, usage string)

Var определяет флаг с указанным именем и строкой использования. Тип и значение флага представлены первым аргументом типа Value, который обычно содержит пользовательскую реализацию Value. Например, вызывающий может создать флаг, который преобразует строку, разделенную запятыми, в набор строк, предоставив набору методы Value; в частности, Set разложит строку, разделенную запятыми, на набор.

Пример

Пример использования flag.Var() с пользовательским типом

package main

import (
	"errors"
	"flag"
	"fmt"
	"strings"
)

// StringSet - пользовательский тип для множества строк
type StringSet map[string]struct{}

// String реализует интерфейс flag.Value
func (s StringSet) String() string {
	return strings.Join(s.ToSlice(), ",")
}

// Set реализует интерфейс flag.Value
func (s StringSet) Set(value string) error {
	if len(s) > 0 {
		return errors.New("StringSet already set")
	}
	
	for _, item := range strings.Split(value, ",") {
		item = strings.TrimSpace(item)
		if item != "" {
			s[item] = struct{}{}
		}
	}
	return nil
}

// ToSlice преобразует StringSet в срез строк
func (s StringSet) ToSlice() []string {
	result := make([]string, 0, len(s))
	for k := range s {
		result = append(result, k)
	}
	return result
}

func main() {
	// Инициализируем наш StringSet
	var tags StringSet = make(map[string]struct{})

	// Регистрируем флаг через flag.Var
	flag.Var(&tags, "tags", "comma-separated list of tags")

	// Парсим аргументы командной строки
	flag.Parse()

	// Выводим результат
	fmt.Println("Tags count:", len(tags))
	fmt.Println("Tags list:", tags.ToSlice())

	// Примеры запуска:
	//   go run main.go -tags=go,programming,example
	//   go run main.go -tags="rust, systems programming"
}

Ключевые компоненты:

  1. Пользовательский тип StringSet:

    • Реализует интерфейс flag.Value с методами String() и Set()
    • Хранит уникальные строки как ключи map
  2. Метод Set():

    • Разбивает строку по запятым
    • Игнорирует пустые элементы
    • Возвращает ошибку при повторной установке
  3. Метод String():

    • Формирует строку из элементов через запятую
    • Используется для вывода значения по умолчанию
  4. Дополнительный метод ToSlice():

    • Удобное преобразование в срез строк

Как это работает:

  1. При вызове flag.Var() пакет flag получает:

    • Указатель на наш StringSet
    • Имя флага (“tags”)
    • Описание для справки
  2. При парсинге аргументов:

    • Для флага -tags=go,programming вызывается Set("go,programming")
    • Наш метод разбивает строку и заполняет map
  3. При выводе справки:

    • Вызывается String() для показа текущего значения

func Visit

func Visit(fn func(*Flag))

Visit посещает флаги командной строки в лексикографическом порядке, вызывая fn для каждого из них. Он посещает только те флаги, которые были установлены.

func VisitAll

func VisitAll(fn func(*Flag))

VisitAll посещает флаги командной строки в лексикографическом порядке, вызывая fn для каждого из них. Он посещает все флаги, даже те, которые не были установлены.

2 - Типы пакета flag

Типы и их функции, включенные в пакет flag

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")
}

Как использовать:

  1. Запуск сервера:
./httpd start -addr :9999 -log-level debug
  1. Остановка сервера:
./httpd stop -timeout 10s -force
  1. Просмотр справки:
./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 создает новый независимый набор флагов, что особенно полезно для реализации:

  1. Подкоманд в CLI-приложениях (например, git commit, docker run)
  2. Изолированных групп флагов
  3. Повторного использования флагов в разных контекстах

Параметры:

  • 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"})
}

Рекомендации по использованию:

  1. Для CLI-приложений используйте flag.ExitOnError
  2. Для библиотек лучше flag.ContinueOnError
  3. Имя набора флагов помогает пользователю понять контекст ошибки
  4. Можно создавать несколько независимых наборов флагов в одной программе

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)

Типичные сценарии использования:

  1. Таймауты операций:
timeout := flag.Duration("timeout", 30*time.Second, "operation timeout")
  1. Интервалы повторения:
interval := flag.Duration("interval", 1*time.Hour, "check interval")
  1. Временные ограничения:
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). Это полезно, когда нужно проверить или изменить поведение обработки ошибок во время выполнения.

Возможные значения:

  1. flag.ContinueOnError - продолжить выполнение после ошибки
  2. flag.ExitOnError - вызвать os.Exit(2) при ошибке
  3. 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)
}

Основные сценарии использования:

  1. Библиотеки - когда нужно передать управление ошибкой вызывающему коду
  2. Тестирование - проверка поведения при разных стратегиях
  3. Гибкие CLI-утилиты - изменение поведения в зависимости от режима работы

Советы:

  1. Для приложений обычно используют ExitOnError
  2. Для библиотек лучше ContinueOnError
  3. 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:

  1. Когда нужно работать с флагами разных типов единообразно
  2. Для создания обобщенных функций обработки флагов
  3. При реализации кастомных типов флагов с возможностью извлечения значения

Важные замечания:

  1. Почти все стандартные типы флагов реализуют Getter
  2. Исключение - флаги, созданные через flag.Func()
  3. Для проверки реализации используйте 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)
}

Комментарии по коду:

  1. Интерфейс Value:

    • String() используется для вывода текущего значения
    • Set() парсит строку и сохраняет значение
  2. Интерфейс Getter:

    • Позволяет получить значение как interface{}
    • Полезно для обобщенной обработки флагов
  3. Особенности реализации:

    • Используем указатель на url.URL для модификации
    • Метод Set() должен быть с pointer receiver
    • Всегда проверяем ошибки парсинга URL
  4. Использование в реальных приложениях:

    // Вместо fs.Parse([]string{...}) используйте:
    fs.Parse(os.Args[1:])
    
  5. Дополнительные улучшения:

    • Можно добавить валидацию URL (например, требовать HTTPS)
    • Можно добавить метод IsSet() для проверки установки значения

Пример запуска:

go run main.go -url "https://example.com/api/v1?param=value"

Вывод:

Parsed URL components:
{scheme: "https", host: "example.com", path: "/api/v1"}

Этот код теперь представляет собой надежную реализацию кастомного флага для работы с URL в Go-приложениях.