ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Elvish flag: 模块完全指南:掌握 Go 约定与 getopt 约定的命令行参数解析

Elvish flag: 模块完全指南:掌握 Go 约定与 getopt 约定的命令行参数解析 CLI编程语言开发工具【免费下载链接】elvishPowerful scripting language versatile interactive shell项目地址https://gitcode.com/gh_mirrors/el/elvish点击查看免费下载flag:是 Elvish 标准库中用于解析命令行标志command-line flags的模块。本文以 website/ref/flag.md 为骨架结合 pkg/mods/flag/flag.go、pkg/getopt/getopt.go 等源码与测试完整讲解两种主流标志语法约定Go 约定与 getopt 约定、三个核心 APIflag:call、flag:parse、flag:parse-getopt的用法与底层原理帮助你为 Elvish 脚本写出健壮、可维护的命令行参数解析逻辑。模块概览一套工具两种语法约定flag:模块为 Elvish 脚本提供命令行标志解析能力它同时支持两种不同的标志约定Go 约定每个标志形如-flagvalue与 Elvish 自身命令elvish以及 Go 标准库flag包的风格一致官方推荐用于 Elvish 脚本getopt 约定标志分短形式-f与长形式--flag两种适合编写包装wrap那些遵循 GNU/BSD getopt 风格的外部程序的脚本。从源码注册看模块通过eval.BuildNsNamed(flag)构建命名空间并向其中注册了三个 Go 函数call、parse、parse-getopt见 pkg/mods/flag/flag.go。该命名空间随后在 pkg/mods/mods.go 中通过ev.AddModule(flag, flag.Ns)挂载为标准库模块因此脚本中直接use flag即可使用。三个函数的定位各不相同函数语法约定典型用途flag:callGo 约定按某个函数的签名解析参数并直接调用该函数最适合做脚本入口flag:parseGo 约定按标志规格specs列表解析参数返回标志值 map 与剩余参数flag:parse-getoptgetopt 约定按规格列表解析短/长标志返回解析出的标志列表与剩余参数接下来先讲解两种约定的语法语义再逐一剖析三个 API。Go 约定-flagvalue与解析停止规则Go 约定下每个标志看起来像-flagvalue。它的核心规则如下布尔标志-flag等价于-flagtrue非布尔标志-flag会把紧随其后的下一个参数当作自己的值即-flag value等价于-flagvalue。解析停止标志解析在遇到第一个非标志参数之前停止或者在遇到标志终止符--之后停止--之后的参数全部视为非标志参数即使它们以-开头。假设-verbose是布尔标志、-port是非布尔标志官方文档给出三个典型示例在-port 10 foo -x中port标志值为10其余参数foo -x都是非标志参数在-verbose 10 foo -x中verbose标志值为true因为它是布尔标志不会吞掉10其余参数10 foo -x都是非标志参数在-port 10 -- -verbose foo中port标志值为10--之后的部分-verbose foo都是非标志参数。此外还支持--flag写法它与-flag完全等价。注意Go 约定不支持单字母标志的链式合并——-rf是一个名为rf的标志而不是-r -f的简写。这是它与 getopt 约定最显著的区别之一。从实现看Go 约定直接复用了 Go 标准库的flag.FlagSetpkg/mods/flag/flag.go 中的newFlagSet创建flag.NewFlagSet(name, flag.ContinueOnError)并将输出丢弃到io.Discard随后调用fs.Parse(args)完成解析addFlag则按默认值的 Go 类型分派到fs.Bool、fs.String或自定义fs.Var详见下文flag:parse一节。因此 Go 约定下的所有行为含--终止、-flag value吞参等都与 Go 标准库flag包一致。getopt 约定短标志、长标志与参数三元组getopt 约定下一个标志可以有以下两种形式可以只有其中一种也可以两者都有短形式单个字符前面加-如-f长形式字符串前面加--如--flag。一个标志可以携带三种参数形态无参数如-f或--flag必需参数如-f value、-fvalue、--flagvalue或--flag value可选参数如-f、-fvalue、--flag或--flagvalue。短标志链式不带参数的短标志后面可以紧跟另一个短标志。例如若-r不带参数则-rf等价于-r -f。链式可以继续延伸后续的短标志如果也不带参数就继续接下一个短标志如果带参数则后面剩余字符就是它的参数。假设-f和-v不带参数、-p带参数则-rfv等价于-r -f -v-rfp80等价于-r -f -p 80。上述链式逻辑在 pkg/getopt/getopt.go 的parseShort中逐字符实现遇到无参数标志就继续循环遇到带参数标志则把剩余字符截取为其参数并停止。getopt 的三个可配置行为getopt 约定的部分行为可以按需开关对应flag:parse-getopt的stop-after-double-dash、stop-before-non-flag、long-only三个选项可选在见到标志终止符--后停止解析标志可选在见到第一个非标志参数之前停止解析标志。关闭此项对应 GNUgetopt_long的行为开启此项对应 BSDgetopt_long的行为可选只支持长标志且长标志允许以单个-开头。开启此项对应getopt_long_only以及 Go 约定的行为。在 pkg/getopt/getopt.go 中这些行为被建模为Config位标志常量StopAfterDoubleDash、StopBeforeFirstNonOption、LongOnly并预置了两个组合值——GNU StopAfterDoubleDash、BSD StopAfterDoubleDash | StopBeforeFirstNonOption。flag:parse-getopt正是通过parseGetoptOptions.Config()见 pkg/mods/flag/flag.go把三个选项翻译成这些位标志再交给getopt.Parse执行。flag:call按函数签名解析并调用flag:call的完整签名见 pkg/mods/flag/flag.d.elv为flag:call $fn $args on-parse-error$nil它按照 Go 约定从$args中解析标志然后调用用户自定义函数$fn。$fn必须是用户定义的函数不能是内置函数或外部命令$fn的每个选项参数opt对应一个标志标志的默认值决定其类型与行为规则与下文flag:parse一致。解析完成后非标志参数按顺序作为$fn的位置参数传入。官方示例~ use flag ~ fn f {|verbose$false port(num 8000) name| put $verbose $port $name } ~ flag:call $f~ [-verbose -port 80 a.c] ▶ $true ▶ (num 80) ▶ a.c ~ flag:call $f~ [-unknown-flag] on-parse-error{|_| echo bad usage } bad usage ~ flag:call $f~ [-verbose a b c] on-parse-error{|_| echo bad usage } bad usage注意第二个与第三个例子前者是非法标志后者是解析后参数个数与$fn期望不符——两者都会触发on-parse-error。on-parse-error是一个函数会收到一个描述错误条件的参数目前该参数应视为不透明值未来会暴露更多有用字段若不提供该选项这类错误会直接作为异常抛出。从源码看pkg/mods/flag/flag.gocall先扫描$fn的选项名与默认值逐个添加标志调用fs.Parse(args)解析出错时若提供了on-parse-error则调用之。解析成功后把标志值收集进 map 作为选项实参非标志参数则通过convertStringArgs转成字符串列表作为位置实参最终fn.Call(fm.Fork(), ...)完成调用当调用因参数个数不匹配errs.ArityMismatch失败时同样会转交on-parse-error。实战用它编写脚本入口flag:call最典型的用途是让 Elvish 脚本接受命令行参数。例如脚本a.elv包含见 pkg/mods/flag/flag.d.elvuse flag fn main { |verbose$false port(num 8000) name| # 脚本主体... } flag:call $main~ $args on-parse-error{|_| echo Usage: (src)[name] [-verbose] [-port PORT-NUM] name exit 1 }随后即可这样运行~ elvish a.elv -verbose -port 80 foo源码注释pkg/mods/flag/flag.go指出了该 API 的一个已知局限on-parse-error虽可输出自定义用法文本但需要手工重复标志名与参数名同时 lambda 签名语法中没有描述选项的合适位置所有标志描述为空无法依赖 Goflag.FlagSet.PrintDefaults的默认帮助文本。官方表示未来可能用不同的 API 解决。仓库自身也大量使用该模式构建脚本 tools/buildall.elv 中main函数通过nameelvish variant keep-bin$false声明选项末尾用flag:call $main~ $args on-parse-error{|_| print $usage; exit 1}解析并调用同时把$usage字符串作为出错时的用法提示输出。flag:parse按规格列表解析返回标志值 mapflag:parse的完整签名见 pkg/mods/flag/flag.d.elv为flag:parse $args $specs$args是要解析的字符串列表$specs是标志规格列表每项形如[ [flag default-value description of the flag] ... ]每个规格由三部分组成标志名不含开头的-、默认值、描述文字。默认值决定标志字符串如何被转换布尔值该标志是布尔标志Go 约定的布尔语义生效。标志值0、f、F、false、False、FALSE转为$false1、t、T、true、True、TRUE转为$true其他值非法。字符串不做任何转换原样保留。类型化数字见 website/ref/language.md标志值通过num转换。列表标志值按,切分等价于{|s| put [(str:split , $s)] }。以上皆非抛出异常。成功时输出两个值一个 map包含$specs中定义的所有标志的值无论是否出现在$args中未出现的取默认值一个列表包含非标志参数。官方示例~ flag:parse [-v -times 10 foo] [ [v $false Verbose] [times (num 1) How many times] ] ▶ [times(num 10) v$true] ▶ [foo] ~ flag:parse [] [ [v $false Verbose] [times (num 1) How many times] ] ▶ [times(num 1) v$false] ▶ []第一个例子演示了-v布尔与-times 10数字的解析第二个例子演示空参数列表时输出默认值。源码默认值驱动的类型分派pkg/mods/flag/flag.go 的addFlag按默认值类型分派bool走fs.Boolstring走fs.Stringint、*big.Int、*big.Rat、float64走自定义的numFlag其Set通过vals.ScanToGo把字符串扫描为vals.Num见 L154-L158vals.List走listFlag其Set把字符串按,切分为列表见 L160-L168其余类型直接返回bad value: flag default value must be boolean, number, string or list异常——这正是测试 pkg/mods/flag/flag_test.elvts 中 map 类型默认值被拒绝的原因。解析完成后fs.VisitAll遍历所有标志把flag.Getter.Get()的结果逐一Assoc进vals.EmptyMapL123-L127从而保证未出现的标志也带默认值出现在结果 map 中。flag:parse-getopt按 getopt 规格解析短/长标志flag:parse-getopt的完整签名见 pkg/mods/flag/flag.d.elv为flag:parse-getopt $args $specs stop-after-double-dash$true stop-before-non-flag$false long-only$false$specs是标志规格列表每项是一个 map[ [shortf longflag arg-optional$false arg-required$false] ... ]规格字段规则short与long分别是不带-/--前缀的短形式与长形式。短形式若非空必须恰好一个字符short与long至少一个非空。arg-optional与arg-required分别声明标志带可选参数还是必需参数两者至多一个为真。规格 map 中出现多余键不是错误会被原样保留并回显。三个选项的语义见上文getopt 的三个可配置行为stop-after-double-dash默认$true控制是否在--后停止stop-before-non-flag默认$false控制是否在首个非标志参数前停止long-only默认$false控制是否只支持长标志且允许单-前缀。成功时输出两个值一个列表描述所有解析出的标志以及一个包含非标志参数的列表。前者形如[ [spec... argvalue long$false] ... ]其中每个条目包含该标志的原始规格spec、其参数arg无参数时为、以及该标志是否以长形式出现long。官方示例部分输出为可读性重新排版~ var specs [ [shortv longverbose] [shortp longport arg-required] ] ~ flag:parse-getopt [-v -p 80 foo] $specs ▶ [[arg long$false spec[longverbose shortv]] [arg80 long$false spec[arg-required$true longport shortp]]] ▶ [foo] ~ flag:parse-getopt [--verbose] $specs ▶ [[arg long$true spec[longverbose shortv]]] ▶ [] ~ flag:parse-getopt [-v] [[shortv extra-infofoo]] # 规格中的多余键 ▶ [[arg long$false spec[extra-infofoo shortv]]] ▶ []源码规格校验与选项映射pkg/mods/flag/flag.go 中每个规格 map 被扫描进specStruct再由OptionSpec()方法校验并转换为getopt.OptionSpecshort与long同时为空返回errShortLongat least one of short and long must be non-emptyarg-required与arg-optional同时为真返回errArgRequiredArgOptionalat most one of arg-required and arg-optional may be true参数形态映射为getopt.NoArgument/RequiredArgument/OptionalArgument三种Arity。解析通过getopt.Parse(args, specs, opts.Config())完成随后把每个结果的Spec回指原始规格 maporiginalSpecMap并组装出包含spec、arg、long的条目列表L244-L258。关联与编辑补全协作flag:parse-getopt的规格设计与编辑器补全 APIedit:complete-getopt同源——后者同样接受$opt-specs每个 map 含short、long、arg-optional、arg-required、desc、completer等键和按位置排列的$arg-handlers函数列表用于在命令行输入-/--时提供选项补全见 pkg/edit/complete_getopt.d.elv。两个 API 共享 getopt 规格心智模型因此你可以在脚本中同时使用flag:parse-getopt解析、edit:complete-getopt补全保持行为一致。测试与验证模块的行为由两组测试保证pkg/mods/flag/flag_test.elvts基于 Elvish 转录transcript的端到端测试覆盖flag:call的布尔/字符串/数字标志、$nil参数与错误回调flag:parse的四种默认值类型、多标志与位置参数混用、非法标志与非法默认值类型flag:parse-getopt的必需/可选参数、三个选项开关stop-after-double-dash、stop-before-non-flag、long-only各自的开/关行为、规格校验错误如[[]]与shortx arg-optional arg-required以及未知选项错误unknown option -x。pkg/mods/flag/flag_test.go通过//go:embed *.elvts *.elv把上述转录文件嵌入测试二进制用evaltest.TestTranscriptsInFS统一驱动执行。此外pkg/getopt/getopt.go 还额外提供Complete函数L140-L184能在参数不完整时返回上下文信息Context及其ContextType供补全引擎使用——这正是它与编辑器补全能力衔接的底层基础。小结flag:模块以两个小而精的语法约定覆盖了绝大多数命令行标志场景Go 约定面向 Elvish 脚本自身-flagvalue、--终止、默认值驱动类型getopt 约定面向包装外部程序短/长标志、链式合并、三种参数形态、三个可开关行为。配合flag:call的按函数签名直接解析调用模式和on-parse-error的用法提示定制你只需几行代码就能为脚本写出专业、健壮的命令行入口edit:complete-getopt则可让同一套规格同时获得交互式补全体验。赞分享CLI编程语言开发工具【免费下载链接】elvishPowerful scripting language versatile interactive shell项目地址https://gitcode.com/gh_mirrors/el/elvish点击查看免费下载相关推荐如何用League Akari智能辅助工具提升英雄联盟游戏效率完整体验指南如何用League Akari智能辅助工具提升英雄联盟游戏效率完整体验指南 League Akari是一款基于GPL 3.0许可证的开源英雄联盟智能辅助工具编程语言语言运行时解释器标准库Go 1.18自定义泛型终极指南从零掌握类型参数与约束Go 1.18自定义泛型终极指南从零掌握类型参数与约束 Go语言在1.18版本正式引入了期待已久的自定义泛型功能 本教程将带你从零开始全面掌握Go泛教程文档后端Jina CLI 完全指南掌握 jina 命令行的架构、命令与参数Jina CLI 完全指南掌握 jina 命令行的架构、命令与参数 本文是 Jina 命令行接口CLI的权威技术指南。Jina 是一个用于构建多模态 AI后端人工智能模型推理服务微服务上一篇用 SCSS 插值Interpolation为 CSS 自定义属性赋值TIL 仓库中的 Sass 实战笔记下一篇Ubuntu 24.04 ROCm 四步安装创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表