Флаг ежедневной библиотеки Go

Go

источник

Я думал о том, как сделать так, чтобы людям было легче поддерживать состояние ежедневного обучения и непрерывной работы. Ведение блога — это один из способов, но не каждый день есть что-то, о чем стоит писать. Иногда технология сложнее.При написании блога я часто пишу, что считаю, что мое понимание предвзято, или детали не до конца поняты.Я должен проверить информацию, а затем продолжить писать после того, как я ее пойму, и скоро. Это может привести к тому, что блог займет слишком много времени.

я просматриваю каждый деньдумаю нет,НаггетсиGithubВ процессе я нашел несколько хороших идей, таких какJS Вопрос дня,Ежедневная библиотека NodeJS,один вопрос интервью в деньИ т. д. и т. д.GitHub.com/par РО-ИТ/Арвин…В этом репозитории собраны небольшие библиотеки NodeJS, увидеть по одной в день не мечта! Это также мое вдохновение для этой серии. Я планирую изучать библиотеку Go каждый день и публиковать вводный пост в блоге. Одна банка в день, конечно, идеальна, и я рассчитываю на 3-5 в неделю.

Сегодня первый день, начнем с базовой библиотеки --- стандартная библиотека в Goflag.

Введение

flagИспользуется для разбора параметров командной строки. Параметры командной строки должны быть знакомы детям, имеющим опыт работы с Unix-подобными системами. например командаls -alСписок сведений обо всех файлах и каталогах в текущем каталоге, где-alявляются параметрами командной строки.

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

  • Укажите путь к файлу конфигурации, напримерredis-server ./redis.confс файлом конфигурации в текущем каталогеredis.confЗапустите сервер Redis;
  • Настройте некоторые параметры, такие какpython -m SimpleHTTPServer 8080Запустите HTTP-сервер, прослушивающий порт 8080. Если не указано, по умолчанию будет прослушиваться порт 8000.

быстрый в использовании

Первым шагом в изучении библиотеки, конечно же, является ее использование. давайте сначала посмотримflagОсновное использование библиотеки:

package main

import (
  "fmt"
  "flag"
)

var (
  intflag int
  boolflag bool
  stringflag string
)

func init() {
  flag.IntVar(&intflag, "intflag", 0, "int flag value")
  flag.BoolVar(&boolflag, "boolflag", false, "bool flag value")
  flag.StringVar(&stringflag, "stringflag", "default", "string flag value")
}

func main() {
  flag.Parse()

  fmt.Println("int flag:", intflag)
  fmt.Println("bool flag:", boolflag)
  fmt.Println("string flag:", stringflag)
}

Вы можете сначала скомпилировать программу, а затем запустить (я использую Win10 + Git Bash):

$ go build -o main.exe main.go
$ ./main.exe -intflag 12 -boolflag 1 -stringflag test

вывод:

int flag: 12
bool flag: true
string flag: test

Если параметр не установлен, соответствующая переменная примет значение по умолчанию:

$ ./main.exe -intflag 12 -boolflag 1

вывод:

int flag: 12
bool flag: true
string flag: default

Вы можете увидеть параметры, которые не установленыstringflagзначение по умолчаниюdefault.

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

$ go run main.go -intflag 12 -boolflag 1

можно использовать-hОтобразить справочную информацию о параметрах:

$ ./main.exe -h
Usage of D:\code\golang\src\github.com\darjun\cmd\flag\main.exe:
  -boolflag
        bool flag value
  -intflag int
        int flag value
  -stringflag string
        string flag value (default "default")

Подводя итог, используйтеflagОбщие шаги для библиотеки:

  • Определите некоторые глобальные переменные для хранения значения параметра, как здесьintflag/boolflag/stringflag;
  • существуетinitиспользуемый методflag.TypeVarпараметры определения метода, здесьTypeможет быть базовым типомInt/Uint/Float64/Bool, а также интервал времениtime.Duration. При определении адреса переменной, имени опции, значения по умолчанию и справочной информации;
  • существуетmainвызов методаflag.Parseотos.Args[1:]варианты разбора. так какos.Args[0]Для пути к исполняемой программе он будет исключен.

будь осторожен:

flag.Parseметод должен вызываться после определения всех опций, иflag.ParseОпции не могут быть определены после вызова. Если вы будете следовать предыдущим шагам, в принципе проблем не будет. так какinitВыполнять перед всем кодом, поместить определения опций вinitсередина,mainвыполнять в функцииflag.ParseВсе параметры уже определены.

формат опции

flagБиблиотека поддерживает три формата параметров командной строки.

-flag
-flag=x
-flag x

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

Первая форма поддерживает только логические параметры, которые отображаются какtrue, не отображается как значение по умолчанию. Третья форма не поддерживает логические параметры. Потому что логические параметры этой формы могут неожиданно вести себя в Unix-подобных системах. См. следующую команду:

cmd -x *

в,*является подстановочным знаком оболочки. Логическая опция, если есть файл с именем 0, false-xвозьметfalse. И наоборот, логические параметры-xвозьметtrue. И эта опция потребляет аргумент. Если вы хотите отобразить, установите логическую опцию наfalse, использовать только-flag=falseэта форма.

Обнаружен первый аргумент, не являющийся опцией (то есть не заканчивающийся на-и--начало) или конец--, синтаксический анализ останавливается. Запустите следующую программу:

$ ./main.exe noflag -intflag 12

выведет:

int flag: 0
bool flag: false
string flag: default

потому что при разборе встречаетсяnoflagпросто стоп, следующие варианты-intflagне разобран. Таким образом, все параметры принимают значения по умолчанию.

Запустите следующую программу:

$ ./main.exe -intflag 12 -- -boolflag=true

выведет:

int flag: 12
bool flag: false
string flag: default

Параметры анализируются в первую очередьintflag, установите его значение равным 12. встретиться--После завершения синтаксического анализа происходит следующее--boolflag=trueне анализируется, поэтомуboolflagзначение опции по умолчаниюfalse.

Если после завершения синтаксического анализа все еще есть аргументы командной строки,flagбиблиотека будет храниться черезflag.ArgsМетод возвращает часть этих параметров. в состоянии пройтиflag.NArgметод для получения количества неразрешенных аргументов,flag.Arg(i)местоположение доступаi(на основе 0). Доступ к количеству опций также можно получить, позвонивflag.NFlagспособ получения.

Немного изменим приведенную выше программу:

func main() {
  flag.Parse()
    
  fmt.Println(flag.Args())
  fmt.Println("Non-Flag Argument Count:", flag.NArg())
  for i := 0; i < flag.NArg(); i++ {
    fmt.Printf("Argument %d: %s\n", i, flag.Arg(i))
  }
  
  fmt.Println("Flag Count:", flag.NFlag())
}

Скомпилируйте и запустите программу:

$ go build -o main.exe main.go
$ ./main.exe -intflag 12 -- -stringflag test

вывод:

[-stringflag test]
Non-Flag Argument Count: 2
Argument 0: -stringflag
Argument 1: test

Разобрать встречу--После завершения остальные параметры-stringflag testСохранить какflag, пройти можноArgs/NArg/Argи другие способы доступа.

Целочисленные значения параметров могут принимать форму 1234 (десятичное), 0664 (восьмеричное) и 0x1234 (шестнадцатеричное) и могут быть отрицательными. Фактическиflagиспользуется внутриstrconv.ParseIntспособ разобрать строку наint. Итак, в теории,ParseIntЛюбой принятый формат подходит.

Значения булевых опций могут быть:

  • ценностьtrue: 1, т, Т, истина, ИСТИНА, Истина;
  • ценностьfalse: 0, f, F, ложь, ЛОЖЬ, Ложь.

Другой способ определения параметров

Выше мы ввели использованиеflag.TypeVarЧтобы определить параметры, этот метод требует, чтобы мы сначала определили переменную, а затем адрес переменной. Другой способ - позвонитьflag.TypeTypeвозможноInt/Uint/Bool/Float64/String/Durationetc) автоматически назначит нам переменную, возвращая адрес этой переменной. Использование аналогично предыдущему способу:

package main

import (
  "fmt"
  "flag"
)

var (
  intflag *int
  boolflag *bool
  stringflag *string
)

func init() {
  intflag = flag.Int("intflag", 0, "int flag value")
  boolflag = flag.Bool("boolflag", false, "bool flag value")
  stringflag = flag.String("stringflag", "default", "string flag value")
}

func main() {
  flag.Parse()
    
  fmt.Println("int flag:", *intflag)
  fmt.Println("bool flag:", *boolflag)
  fmt.Println("string flag:", *stringflag)
}

Скомпилируйте и запустите программу:

$ go build -o main.exe main.go
$ ./main.exe -intflag 12

выведет:

int flag: 12
bool flag: false
string flag: default

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

Расширенное использование

определить короткие параметры

flagБиблиотека не поддерживает короткие параметры, но это можно сделать, установив разные параметры для одной и той же переменной. То есть оба варианта используют одну и ту же переменную. Поскольку порядок инициализации не определен, они должны иметь одинаковое значение по умолчанию. В противном случае поведение не определено, если этот параметр не передан.

package main

import (
  "fmt"
  "flag"
)

var logLevel string

func init() {
  const (
    defaultLogLevel = "DEBUG"
    usage = "set log level value"
  )
  
  flag.StringVar(&logLevel, "log_type", defaultLogLevel, usage)
  flag.StringVar(&logLevel, "l", defaultLogLevel, usage + "(shorthand)")
}

func main() {
  flag.Parse()

  fmt.Println("log level:", logLevel)
}

Скомпилируйте и запустите программу:

$ go build -o main.exe main.go
$ ./main.exe -log_type WARNING
$ ./main.exe -l WARNING

Используйте как длинные, так и короткие параметры для вывода:

log level: WARNING

Если эта опция не передана, выводится значение по умолчанию:

$ ./main.exe
log level: DEBUG

интервал времени парсинга

Помимо возможности использовать примитивные типы в качестве опций,flagБиблиотека также поддерживаетtime.DurationТип, который является временным интервалом. Для временных интервалов поддерживается множество форматов, таких как «300 мс», «-1,5 ч», «2 ч 45 мин» и т. д. Единица времени может быть ns/us/ms/s/m/h/day и т.д. Фактическиflagбудет вызываться внутреннеtime.ParseDuration. Конкретные поддерживаемые форматы см.time(требуется fq) Документация к библиотеке.

package main

import (
  "flag"
  "fmt"
  "time"
)

var (
  period time.Duration
)

func init() {
  flag.DurationVar(&period, "period", 1*time.Second, "sleep period")
}

func main() {
  flag.Parse()
  fmt.Printf("Sleeping for %v...", period)
  time.Sleep(period)
  fmt.Println()
}

Согласно входящим параметрам командной строкиperiod, программа засыпает соответствующее время, по умолчанию 1 секунда. Скомпилируйте и запустите программу:

$ go build -o main.exe main.go
$ ./main.exe
Sleeping for 1s...

$ ./main.exe -period 1m30s
Sleeping for 1m30s...

пользовательские параметры

Помимо использованияflagТип параметра, предоставляемый библиотекой, мы также можем настроить тип параметра. Разберем кейсы, представленные в стандартной библиотеке:

package main

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

type interval []time.Duration

func (i *interval) String() string {
  return fmt.Sprint(*i)
}

func (i *interval) Set(value string) error {
  if len(*i) > 0 {
    return errors.New("interval flag already set")
  }
  for _, dt := range strings.Split(value, ",") {
    duration, err := time.ParseDuration(dt)
    if err != nil {
      return err
    }
    *i = append(*i, duration)
  }
  return nil
}

var (
  intervalFlag interval
)

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

func main() {
  flag.Parse()

  fmt.Println(intervalFlag)
}

Сначала определите новый тип, здесь определите типinterval.

Новый тип должен реализоватьflag.Valueинтерфейс:

// src/flag/flag.go
type Value interface {
  String() string
  Set(string) error
}

вStringметод для форматирования значения этого типа,flag.ParseКогда метод встречает параметр пользовательского типа во время выполнения, он вызывает переменную этого типа со значением параметра в качестве параметра.Setметод. здесь будет,Отдельные временные интервалы анализируются и сохраняются в срезе.

Определения опций пользовательского типа должны использоватьflag.Varметод.

Скомпилируйте и выполните программу:

$ go build -o main.exe main.go
$ ./main.exe -deltaT 30s
[30s]
$ ./main.exe -deltaT 30s,1m,1m30s
[30s 1m0s 1m30s]

Если указанное значение опции недопустимо,Setметод возвращаетerrorзначение типа,ParseВыполнение завершается, печатая ошибки и используя справку.

$ ./main.exe -deltaT 30x
invalid value "30x" for flag -deltaT: time: unknown unit x in duration 30x
Usage of D:\code\golang\src\github.com\darjun\go-daily-lib\flag\self-defined\main.exe:
  -deltaT value
        comma-seperated list of intervals to use between events

Разобрать строку в программе

Иногда параметры не передаются в командной строке. Например, чтение из таблицы конфигурации или сгенерированное программой. В это время вы можете использоватьflag.FlagSetstruct для анализа этих параметров.

На самом деле то, что мы называли ранееflagМетоды библиотеки будут вызываться косвенноFlagSetметод структуры.flagБиблиотека определяетFlagSetглобальная переменная типаCommandLineСпециально для разбора параметров командной строки. ранее называлсяflagМетоды библиотеки просто для удобства, они вызываются внутриCommandLineСоответствующий метод для:

// src/flag/flag.go
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

func Parse() {
  CommandLine.Parse(os.Args[1:])
}

func IntVar(p *int, name string, value int, usage string) {
  CommandLine.Var(newIntValue(value, p), name, usage)
}

func Int(name string, value int, usage string) *int {
  return CommandLine.Int(name, value, usage)
}

func NFlag() int { return len(CommandLine.actual) }

func Arg(i int) string {
  return CommandLine.Arg(i)
}

func NArg() int { return len(CommandLine.args) }

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

package main

import (
  "flag"
  "fmt"
)

func main() {
  args := []string{"-intflag", "12", "-stringflag", "test"}

  var intflag int
  var boolflag bool
  var stringflag string

  fs := flag.NewFlagSet("MyFlagSet", flag.ContinueOnError)
  fs.IntVar(&intflag, "intflag", 0, "int flag value")
  fs.BoolVar(&boolflag, "boolflag", false, "bool flag value")
  fs.StringVar(&stringflag, "stringflag", "default", "string flag value")

  fs.Parse(args)
  
  fmt.Println("int flag:", intflag)
  fmt.Println("bool flag:", boolflag)
  fmt.Println("string flag:", stringflag)
}

NewFlagSetМетод имеет два параметра, первый параметр — это имя программы, которое отображается при выводе справки или ошибок. Второй параметр что делать при разборе ошибок, есть несколько вариантов:

  • ContinueOnError: продолжить синтаксический анализ после возникновения ошибки,CommandLineиспользовать эту опцию;
  • ExitOnError: вызывается при ошибкеos.Exit(2)выйти из программы;
  • PanicOnError: паника при возникновении ошибки.

посмотриflagСоответствующий код в библиотеке:

// src/flag/flag.go
func (f *FlagSet) Parse(arguments []string) error {
  f.parsed = true
  f.args = arguments
  for {
    seen, err := f.parseOne()
    if seen {
      continue
    }
    if err == nil {
      break
    }
    switch f.errorHandling {
    case ContinueOnError:
      return err
    case ExitOnError:
      os.Exit(2)
    case PanicOnError:
      panic(err)
    }
  }
  return nil
}

с прямым использованиемflagПодход библиотеки немного отличается,FlagSetперечислитьParseМетод должен отображать входящий фрагмент строки в качестве параметра. так какflag.Parseзвонил внутреннеCommandLine.Parse(os.Args[1:]). Пример кода находится вGitHubвверх.

Ссылаться на

  1. flagбиблиотечная документация

я

мой блог

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~

Эта статья опубликована в блогеOpenWriteвыпуск!