ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

fish-shell 的 argparse 内建命令完全指南:从 Option Spec 到标志值校验

fish-shell 的 argparse 内建命令完全指南:从 Option Spec 到标志值校验 CLI开发工具【免费下载链接】fish-shellThe user-friendly command line shell.项目地址https://gitcode.com/GitHub_Trending/fi/fish-shell点击查看免费下载argparse是 fish-shell 内置的命令行参数解析器专门用于让 fish 脚本与函数以声明式的方式处理选项options与位置参数positional arguments。与 POSIX 的getopt(3)外部命令不同它是 fish 的内建命令builtin可以直接在当前函数作用域内设置局部变量无需通过 stdout 输出再eval。读完本文你将掌握 Option Spec 领域专用语言DSL的完整语法、argparse自身全部选项的语义、标志值校验Flag Value Validation机制并能用$argv/$argv_opts写出可转发参数的健壮包装函数。argparse 是什么一条命令管好 fish 脚本的参数在 fish 中脚本和函数通过$argv接收调用者传入的参数。argparse命令让这一过程变得简单你传入定义已知选项的参数OPTION_SPEC紧跟一个字面的--然后是待解析的参数这些参数里也可能包含另一个字面的--。解析完成后每个被识别到的选项会生成名为_flag_X的变量其中X是短选项字母与长选项名若定义了两者则各生成一个$argv_opts被设置为解析到的选项及其值$argv被设置为剩余的非选项参数。变量以局部作用域local scope被设置等价于脚本里执行了set -l _flag_X。若标志是布尔型传了就是 1不带值变量的值是实际见到的短/长标志形式若选项带值则变量保存零个或多个收集到的值若某标志从未出现对应的_flag_X变量就不会被设置。完整的调用语法为文档原文argparse [OPTIONS] OPTION_SPEC ... -- [ARG ...]argparse 自身的选项以下选项属于argparse命令本身它们必须出现在所有OPTION_SPEC之前选项含义-n/--name NAME在错误消息中使用NAME。默认使用当前函数名若在函数外运行则用argparse-x/--exclusive OPTIONS逗号分隔的互斥选项列表可多次使用以定义多组互斥关系。只需给出各选项的短或长形式之一选项本身仍需另行定义-N/--min-args NUMBER可接受的非选项参数最小个数默认 0-X/--max-args NUMBER可接受的非选项参数最大个数默认无穷大-u/--move-unknown允许未知选项并将其从$argv移到$argv_opts。默认情况下未知选项被当作带可选参数处理如同其选项规格为?-i/--ignore-unknown已废弃。类似--move-unknown但未知选项及其参数保留在$argv中、不进入$argv_opts。与--move-unknown不同它使“未知选项”和“以-开头的非选项参数”无法区分因为$argv中的--分隔符会被移除-S/--strict-longopts让长选项解析更严格详见下文--unknown-arguments KIND隐式启用--move-unknown除非同时给了--ignore-unknown并按KIND修改未知选项的解析行为-s/--stop-nonopt遇到第一个非选项参数即停止扫描常用于实现自带选项的子命令-h/--help显示用法帮助从源码看这些选项在 src/builtins/argparse.rs 中通过SHORT_OPTIONShn:siuU:x:SN:X:与LONG_OPTIONS表注册其中i/u互斥若同时出现会报 “options cannot be used together” 错误Error::COMBO_EXCLUSIVE。--unknown-arguments的合法取值为optional/required/none分别对应ArgType::OptionalArgument/RequiredArgument/NoArgument非法取值会直接报错退出。--unknown-arguments 的三种取值该选项会改变未知选项的参数绑定行为optional默认允许每个未知选项带可选参数如同其规格是?或*。例如argparse --ignore-unknown --unknown-argumentsoptional ab -- -u -a -ub会设置_flag_a但不会设置_flag_b因为b被当作第二次-u的参数required要求每个未知选项必须带参数如同其规格是或。同样的例子改用--unknown-argumentsrequired后_flag_a和_flag_b都不会被设置-a被当作第一次-u的参数b被当作第二次-u的参数。注意若参数以未知选项结尾会报错因为它没有参数none禁止未知选项带参数如同其规格没有。上述例子下_flag_a与_flag_b都会被设置。此时若使用--flagvalue语法且flag是未知选项会报错。另需注意以上讨论假设未知长选项使用--的 GNU 风格。例如KIND为none且不存在bar长选项时-bar会被解释为三个短标志b、a、r但如果bar是已知选项-bar与--bar等价。--move-unknown 的边界行为-u模式下一个值得注意的细节文档原文如果一组短选项里未知短选项跟在已知短选项之后那么已知短选项会被当作未知选项的参数。例如argparse --move-unknown h -- -oh会把h当作-o的参数_flag_h不会被设置反之若已知选项在前且不带参数则会被正常识别argparse --move-unknown h -- -ho会将$_flag_h设为-h。--strict-longopts 严格长选项模式不加该标志时若long是已知长选项--long与--longvalue可以被缩写为-long与-longvalue——但仅当不存在短标志l时--lo与--lovalue——但仅当没有其他以lo开头且未被占用的长选项时其他任何非空前缀同理-lo与-lovalue即上面两者的组合。加上--strict-longopts后以上三种写法都会成为解析错误必须使用完整的--long或--longvalue语法。该标志对未知选项的解析没有影响未知选项始终按严格模式解析。文档也提醒此选项未来可能默认开启因此不要依赖非严格行为。基本用法与流程使用方式固定为先给OPTION_SPEC再给一个强制性的--最后是待解析参数。最简单的例子argparse h/help n/name -- $argv or return若$argv为空则没有可解析的内容argparse返回 0 表示成功若$argv非空则检查是否包含-h、--help、-n、--name等标志找到后将其从参数中移除并设置局部变量_flag_OPTION供脚本判断哪些选项出现过若$argv没有任何错误如未知选项、选项缺少必填值argparse以状态 0 退出否则向 stderr 写入错误消息并以状态 1 退出。这里的or return意味着若argparse失败函数直接返回其失败状态只有argparse成功才会继续往下执行。解析完成后检查标志的惯用写法文档原文# 检查 _flag_h 和 _flag_help 是等价的 # 我们检查它是否至少被给定一次 if set -ql _flag_h echo Usage: my_function [-h | --help] [-n | --nameNAME] 2 return 1 end set -l myname somedefault set -ql _flag_name[1] and set myname $_flag_name[-1] # 这里使用*最后一个* --name标志名中不适用于变量名的字符如-连字符会被替换为下划线。这也是文档示例里--dry-run要用_flag_dry_run判断的原因——源码 set_argparse_result_vars 正是把所有非字母数字字符映射为_后再构造_flag_变量名。--分隔符为什么必须存在--参数是必需的。你可以不提供任何选项规格或待解析参数但必须写上--set -l argv foo argparse h/help n/name -- $argv argparse --min-args1 -- $argv以下写法是非法的set -l argv argparse h/help n/name $argv因为第一个--是argparse可靠地区分“它自身的选项”如--move-unknown与“命令参数”的关键故不可省略。这在源码 collect_option_specs 中也有体现扫描选项规格时若先遇到参数末尾而未见到--会直接报 “Missing -- separator”。Option Specifications选项规格领域专用语言每个选项规格由以下几部分构成文档原文可选的字母数字短标志字符可选的、以/开头分隔的长标志名。若短标志和长标志都不存在则报错若没有短标志且长标志名多于一个字符/可以省略为向后兼容若同时有短标志和长标志可以用-代替/此时该短标志不能被用户使用也不会暴露为标志变量参数行为修饰符若无修饰则是布尔标志或整数标志要求一个值且只保存该标志最后一次出现的值?接受可选值且只保存最后一次出现的值要求一个值且每次出现都保存*接受可选值且每次出现都保存未给值时存空字符串可选地跟一个表示该选项及其附带的值不保存进$argv或$argv_opts但不影响_flag_变量可选地跟!加一段 fish 脚本来校验值通常是调用某个函数退出码为 0 表示值有效非零表示无效。校验错误消息应写到stdout而非 stderr。详见下文“Flag Value Validation”。选项规格中的字符修饰顺序在源码 parse_flag_modifiers 中按及?//*、、!依次解析非法字符会以 “Invalid option spec” 报错并指明出错位置。若一个标志在解析参数时未被看到对应的_flag_X变量就不会被设置。更多可读但更冗长的选项规格生成方式见 fish_opt 命令——它通过-s/-l/-o/-r/-m/-d/-v等参数拼出与手写等价的规格字符串。整数标志Integer flag有些命令直接用数字作为选项如foo -55。为此选项规格可带#修饰符任何整数都会被理解为该标志最后一个数字会作为其值如同使用了。#必须紧跟短标志字母如果有且不允许再使用等其他修饰符-除外为向后兼容m#maximum注意它只读取形如-NNN的“像标志一样”的数字不读取NNN。源码中 is_implicit_int 通过fish_wcstol判断参数能否解析为整数来决定是否命中隐式整数选项。可选参数Optional arguments的绑定规则用?或*定义的选项可以接受可选参数但可选参数必须直接附着在选项上cmd --flagvalue # 或 cmd -fvalue而cmd --flag value中value会被当作位置参数--flag不带参数。这是有意设计否则在还想使用位置参数的情况下不带可选参数地使用选项会非常困难。经典例子是 GNU grep 的实际行为文档原文grep --color auto # 这里 auto 被用作搜索字符串 # color 不带参数、回退到默认值恰好也是 auto。 grep --color always # grep 仍只使用颜色 auto并搜索字符串 always。这不只是 argparse 的特性而是所有使用getopt(3)的工具如果支持可选参数的共同约定。Flag Value Validation用 fish 脚本校验选项值有时你需要校验选项值例如必须是特定范围内的整数、必须是合法的 IP 地址等。你可以在argparse返回后自行校验也可以让argparse通过执行任意 fish 脚本来完成校验在选项规格末尾追加!感叹号再接要运行的脚本。执行时会有三个局部导出变量被定义_argparse_cmdargparse --name的值_flag_name正在处理的短或长标志_flag_value与该标志关联的值。校验脚本应把错误消息写到stdout不是 stderr并返回状态 0值有效或非零值无效。在源码 validate_arg 中这三个变量通过EnvMode::LOCAL_EXPORTED在临时作用域中设置校验脚本经exec_subshell执行其捕获的输出会被追加到 stderr。fish 内置了_validate_int函数share/functions/_validate_int.fish接受--min与--max标志。例如你的命令接受-m/--max标志且合法值范围是 0 到 5可这样定义选项m/max!_validate_int --min 0 --max 5不带这两个标志调用_validate_int时默认只检查值是否为合法整数不限制取值范围。其实现先用string match -qr ^-?\d$校验整数格式再分别与_flag_min/_flag_max比较出错时向 stderr 打印带_argparse_cmd、_flag_name的本地化错误消息。更多校验示例文档原文# 校验某路径是目录 argparse p/path!test -d $_flag_value -- --path $__fish_config_dir # 校验某个函数不存在 argparse f/func!not functions -q $_flag_value -- -f alias # 校验字符串匹配正则如十六进制颜色 argparse c/color!string match -rq \^#?[0-9a-fA-F]{6}$\ $_flag_value -- -c c0ffee # 用校验函数 argparse n/num!_validate_int --min 0 --max 99 -- --num 42OPTION_SPEC 示例逐条解读文档给出了大量规格示例逐个说明其含义文档原文h/help-h与--help都合法布尔标志可多次使用。任一形式出现时_flag_h与_flag_help都会按“每次见到哪种形式”依次记录例如-h、-h、--help此时count $_flag_h得到 3help只有--help合法布尔标志可多次使用设置_flag_help。另有h-help任意短字母写法用于向后兼容help与help类似会把--help从$argv中移除区别是--help不会被放入$argv_optslongonly标志--longonly要求一个值没有短标志也没有短标志变量n/name-n与--name都合法要求一个值最多使用一次若被看到_flag_n与_flag_name都设置成该唯一值n/name?-n与--name都合法接受可选值最多使用一次若提供值则设置否则两个变量都不带值被设置n/name*类似?但可多次使用每个出现的值都会被记录未给值的出现记录为空字符串name只有--name合法要求值且可多次使用每次出现的值都会记录到_flag_namex只有-x合法布尔标志可多次使用/x与x类似但只有--x合法而非-xx、x?、x与 n/name 系列类似但没有长标志替代形式#max或#-max匹配正则^--?\d$的标志都合法被看到时赋给变量_flag_max。这允许任意正负整数以单个-前缀的形式给出许多命令都支持这一惯用法例如head -3 /a/file只输出文件前 3 行n#max同样匹配^--?\d$赋给_flag_n与_flag_max两种形式均可指定值-n NNN或--max NNN#longonly最后一个整数选项被存入_flag_longonly。解析结束后$argv被设为局部作用域、内容是未被标志处理消耗的剩余值没有剩余值时变量仍被设置但count $argv为 0。$argv_opts同理保存的是被消耗进标志处理的参数——这让$argv_opts可以被整体转发给另一个命令再附上额外参数。实战示例一简单的帮助标志argparse h/help -- $argv or return if set -q _flag_help # TODO: 在此打印帮助 return 0 end这只支持一个选项-h/--help任何其他选项都是错误若给了它则打印帮助并退出文档原文。对应行为在测试 tests/checks/argparse.fish 中大量覆盖例如未传--报 “Missing -- separator”、非法规格报 “Invalid option spec h- at char -” 等错误分支。实战示例二fish_add_path 如何解析参数fish 自带函数 share/functions/fish_add_path.fish 就是argparse的真实使用者argparse -x g,U -x P,U -x a,p g/global U/universal P/path p/prepend a/append h/help m/move v/verbose n/dry-run -- $argv这里有一批布尔标志都带长短两种形式。其中一部分不能同时使用这正是-x的用途-x g,U表示--global与--universal或其短形式互斥同时使用会得到错误。使用-x时只需给出短或长形式之一无需给出完整选项规格。随后它根据--path标志决定操作哪个变量文档原文set -l var fish_user_paths set -q _flag_path and set var PATH # ... # 检查 --dry-run。 # - 已被替换为 _因为它不是合法的变量名字符 not set -ql _flag_dry_run and set $var $result在源码层互斥检查由 check_for_mutually_exclusive_flags 完成遍历每个num_seen 0的选项若其属于某个互斥集合且集合内另一个选项也被见过则以 “options cannot be used together” 报错为让单元测试可预测错误里两个标志的先后顺序会被确定性排序。parse_exclusive_argsargparse.rs则负责把-x的逗号分隔字符串解析为互斥集合少于两个标志的字符串或未定义的标志都会报错。实战示例三用 $argv_opts 写包装函数下面的my-head包装函数展示了$argv_opts的典型用法把已知选项原样转发给真正的head同时新增/改写选项文档原文function my-head # 下面是 head 唯一一个带参数的现有选项 # 我们将原样转发它。 set -l opt_spec n/lines # --qwords 是新选项而 --bytes 是现有选项我们将在下面改写它 set -a opt_spec qwords c/bytes argparse --strict-longopts --move-unknown --unknown-argumentsnone $opt_spec -- $argv || return if set -q _flag_qwords # --qwords 允许以 8 字节的倍数指定大小 set -a argv_opts --bytes(math -- $_flag_qwords \* 8 || return) else if set -q _flag_bytes # 允许 q 后缀例如 --bytes4q 表示 4*8 字节。 if string match -qr q$ -- $_flag_bytes set -a argv_opts --bytes(math -- (string replace -r q$ *8 -- $_flag_bytes) || return) else # 保留用户的设置 set -a argv_opts --bytes$_flag_bytes end end if test (count $argv) -eq 0 # 默认读取 /dev/kmsg而 head 默认读 stdin set -l argv /dev/kmsg end # 用修改后的选项和参数调用真正的 head。 head $argv_opts -- $argv end这段代码的要点所有不想亲自处理的选项都留在$argv_opts中--qwords和--bytes因规格以结尾而不保存在那里随后用$_flag_OPTION变量处理它们并把变换后的选项追加回$argv_opts其中已包含除--qwords、--bytes外的全部原始选项因为调用用了--move-unknown与--unknown-argumentsnone只需告知argparse那些带参数的head选项包装函数就能准确算出非选项参数即$argv也就是head要操作的文件名若改用--unknown-argumentsoptional并显式列出head的全部已知选项好处是将来head新增选项时包装脚本仍可用“粘连”形式如-oarg或--optarg继续使用它们--strict-longopts是必要的不加它时my-head -q --bytes 10q会把-q解析成--qwords的简写从而出错。从源码看实现解析管线与变量落地argparse的 Rust 实现src/builtins/argparse.rs整体分四步解析自身选项parse_cmd_opts用WGetopter处理-n、-x等控制选项--unknown-arguments隐式启用--move-unknown的逻辑也在这里完成未给--name时默认取当前函数名parser.get_function_name(1)收集选项规格collect_option_specs逐条解析OPTION_SPEC直至--长选项名与短字母建立long_to_short_flag映射纯长选项由计数器分配 Unicode 私用区字符从0xE000起上限0xF8FF约 6400 个作为内部短标志解析待处理参数argparse_parse_flags按需为每个选项动态构造 getopt 短/长选项串populate_option_strings逐参数消费布尔标志把所见形式-x或--long记录进vals带值标志经过 validate_arg 校验后按“只存最后一个”或“累积全部”写入未知选项按UnknownHandlingError/Ignore/Move与unknown_arguments类型分流修饰的选项经 delete_flag 从$argv_opts中摘除同时正确处理“短选项组中摘出单个标志并放回其余部分”的细节落盘变量set_argparse_result_vars把所有num_seen 0的选项写为_flag_局部变量长选项名非字母数字字符替换为_最后设置局部$argv与$argv_opts。最终在 argparse 主函数 中依次执行上述步骤并在末尾通过 check_min_max_args_constraints 校验--min-args/--max-args约束不满足时报expected N arguments或expected N arguments。测试套件 tests/checks/argparse.fish 以 846 行的断言覆盖了错误分支缺--、非法规格、短/长/整数标志重复定义、隐式整数标志带修饰符等与正常分支_flag_取值、$argv/$argv_opts分布、--stop-nonopt、隐式整数、校验成功与失败、ignore/move unknown 等是理解各选项语义最直接的参考。交互式补全方面share/completions/argparse.fish 为-x/--exclusive提供了基于已写选项规格动态列出“未使用选项”的补全助手并约束-i与-u互斥补全。小结argparse是 fish 脚本开发中高频使用的内建命令OPTION_SPEC的紧凑 DSL 让你在一行内定义短/长标志、必填/可选/累积参数、摘除与!校验-x、-N/-X、-S、--unknown-arguments等控制选项覆盖互斥、参数个数、严格长选项与未知选项处理等进阶需求$argv与$argv_opts的分离则让包装类函数可以优雅地转发选项。无论是编写自用函数还是复刻fish_add_path、my-head这类生产级包装器掌握本文覆盖的语法与语义都能让你的 fish 脚本参数处理既健壮又易维护。赞分享CLI开发工具【免费下载链接】fish-shellThe user-friendly command line shell.项目地址https://gitcode.com/GitHub_Trending/fi/fish-shell点击查看免费下载相关推荐探索Hermes Agent的自然语言推理能力逻辑与常识推理的终极指南探索Hermes Agent的自然语言推理能力逻辑与常识推理的终极指南 Hermes Agent作为一款强大的AI智能体工具不仅具备工具调用能力、交互式命令AI Agent人工智能AI 应用工具调用Agent 记忆交互助手RAG任务调度MCP 服务深度解析企业级文档智能处理框架WeKnora的5大核心特性与架构设计深度解析企业级文档智能处理框架WeKnora的5大核心特性与架构设计 WeKnora是一款基于LLM技术的企业级智能知识管理框架专为文档理解、语义检索和上下人工智能大模型RAGAI Agent后端前端MCP 服务知识库dsh-plugin工具调用dlt / dlthub 命令行接口CLI全参考从 argparse 解析到每个子命令的完整指南dlt / dlthub 命令行接口CLI全参考从 argparse 解析到每个子命令的完整指南 本文是基于 dlt 开源仓库中 command line数据工程数据集成批处理上一篇LogicFlow5分钟快速上手的业务流程图开发解决方案下一篇猫抓浏览器扩展5分钟掌握视频资源嗅探与下载技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表