Go 命令行解析 flag 包之快速上手

请添加图片描述

本篇文章是 Go 标准库 flag 包的快速上手篇。

概述

开发一个命令行工具,视复杂程度,一般要选择一个合适的命令行解析库,简单的需求用 Go 标准库 flag 就够了,flag 的使用非常简单。

当然,除了标准库 flag 外,也有不少的第三方库。比如,为了替代 flag 而生的 pflag,它支持 POSIX 风格的命令行解析。关于 POSIX 风格,本文末尾有个简单的介绍。

更多与命令行处理相关的库,可以打开 awesome-go#command-line 命令行一节查看,star 最多的是 spf13/cobra 和 urfave/cli ,与 flag / pflag 相比,它们更加复杂,是一个完全的全功能的框架。

有兴趣都可以了解下。

目标案例

回归主题,继续介绍 flag 吧。通过案例介绍包的使用会比较直观。

举一个例子说明吧。假设,现在要开发一个 Go 语言环境的版本管理工具,gvg(go version management by go)。

命令行的帮助信息如下:

NAME:
   gvg - go version management by go

USAGE:
   gvg [global options] command [command options] [arguments...]

VERSION:
   0.0.1

COMMANDS:
   list       list go versions
   install    install a go version
   info       show go version info
   use        select a version
   uninstall  uninstall a go version
   get        get the latest code
   uninstall  uninstall a go version
   help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version

这个命令不仅包含了全局的选项,还有 8 个子命令,部分子命令支持参数和选项。暂时,子命令的选项参数先不列出来了,实现时再看。

接下来,我们试着通过 flag 实现这个效果。本文只介绍 GLOBAL OPTIONS(全局选项)的实现。

如果想了解什么是 Go 语言环境的版本管理,可以查看 如何灵活地进行 Go 版本管理 一文。

选项表示

最简单的命令不需要任何参数和选项,复杂一点,要支持参数和选项的配置。gvg 没有全局参数,或者说全局参数是子命令,全局选项有 --help -h--version -h

一个选项在 flag 包中用一个 Flag 表示,那 -h 可以用一个 Flag 表示。一个选项通常由几个部分组成,如名称、使用说明和默认值。如果将 -h 用代码表示,如下:

h := flag.Bool("h", false, "show help")

定义了一个布尔类型的 Flag,名为 h,默认值是 false,使用说明为 “show help”。变量 h 是一个布尔型的指针,通过它可以取出命令行传入的值。

除了使用 flag.Bool,还可以使用另外一种方式,Flag.BoolVar 定义一个 Flag。我们可以用这种方式定义 -v 选项。

代码如下:

var v bool
flag.BoolVar(&v, "v", false, "print the version")

最后的三个参数含义与 flag.Bool 相同,主要区别在值的获取方式,flag.BoolVar 是通过将变量地址传入获取值。从经验来看,第二种方式使用的较多,或许因为第一种方式会发生变量逃逸。

更多类型

除了布尔类型,Flag 的类型还有整数(int、int64、uint、uint64)、浮点数(float64)、字符串(string)和时长(time.Duration)。

假设 gvg 的案例中,支持配置文件选项 --config-path。实现代码如下:

var configPath

flag.StringVar(&configPath, "config-path", "", "config file path")

通过 StringVar 定义了新的 Flag。使用方式与 BoolVar 相同,最后的三个参数分别是选项名称、默认值和使用说明。

虽然 flag 支持的内置类型并不多,但已经满足大部分需求了。如果有自定义的需求,也可以扩展新的类型实现,这部分内容下篇介绍。

长短选项

现在已经完成了 -h-v 两个选项,但目标是 -v --version-h --help,即同时支持长短选项。

一个 Flag 应该有长短两种形式,但 flag 包并不支持这种风格,需要曲线救国才能实现。(注:本文开开头提到的 pflag 支持。)

这里以 -v --version 为例,代码如下:

flag.BoolVar(&v, "v", false, "print the version")
flag.BoolVar(&v, "version", false, "print the version")

定义了两个 Flag,同时绑定到了一个变量上。这种效果只能用 flag.BoolVar 方式定义新的 Flagflag.Bool 没办法做到将同一个变量同时绑定两个 Flag

但其实这种也有缺点,先不说了,后面介绍帮助信息打印时就明白了。

命令行解析

定义好所有 Flag,还需要一步解析才能拿到正确的结果。这一步非常简单,调用 flag.Parse() 即可。

如下是完整的代码:

package main

var h *bool
var v bool

func init() {
	flag.BoolVar(&h, "h", false, "show help")
	flag.BoolVar(&h, "help", false, "show help")
	flag.BoolVar(&v, "v", false, "print the version")
	flag.BoolVar(&v, "version", false, "print the version")
}

func main() {
	flag.Parse()
	fmt.Println("version", v)
	fmt.Println("help", h)
}

通过 flag.Parse() 解析完成,打印下 vh 变量,确认下是否成功获取到了值。

到此,代码就告一段落了,现在将它编译为 gvg 命令吧。

使用命令

在正式使用命令前,先介绍下 flag 的语法。官方文档说明,命令行中 flag 选项的使用语法有如下几种形式。

-flag
-flag=x
-flag x // 非布尔类型才支持这种方式

但其实,-- 也是支持的。因此,上面才可以实现 --version 的曲线救国。

使用下这个命令,将 help 设置为 falseversion 设置为 true。我尽量把所有可能的写法都列出来。

$ gvg -v
$ gvg -version -h=false  # 单个 - ,即 -version 支持
$ gvg --version=true --help=false
$ gvg --version=1 --help=0
$ gvg --version=t --help=f
$ gvg --version=T --help=F
$ gvg --version true --help true # 写法错误,因为无法识别出是 bool 值,还是参数或子命令
$ gvg -vh  # 不支持这种风格

执行命令,输出结果:

version true
help false

到这里,flag 的快速入门就介绍完了。参数留在子命令的时候介绍。

命令行风格

由于一些历史原因,Unix 出现过很多不同的分支,命令行的风格也因此有很多标准,比如:

  • Unix 风格,选项采用单 - 加一个字母,比如 -v,短选项就是它,优点是足够简洁;
  • BSD 风格,选项没有 -,没有任何的前缀,不知道有参数的情况怎么处理,没有研究;
  • GNU 风格,采用 --,如 --version,长选项,扩展性好,但是要多打几个字母;

在网上找到一个搞笑漫画。

请添加图片描述

查看系统进程有两种写法, ps aux(BSD 风格) 和 ps -elf(Unix 风格)。之前,我一直很郁闷为什么有这个区别。现在算是明白了。哈哈。

POSIX 的命令行风格算是取长补短的集合吧。什么是 POSIX 风格?可以查看这篇文档 命令参数语法。它同时提供了长短选项的标准。

要明白的是,标准终究只是标准,很多命令其实并不遵循它。但自己在设计命令行规范的时候,最好还是要有一套标准,而参考最统一的标准肯定是没错的。

总结

本文介绍了 Go 中 flag 包的使用,一般的场景已经足够使用了。

博文地址:Go 命令行解析 flag 包之快速上手

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/352847.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

架构整洁之道——价值维度与编程范式

1 设计与架构究竟是什么 结论:二者没有任何区别,一丁点区别都没有。 架构图里实际上包含了所有底层设计细节,这些细节信息共同支撑了顶层的架构设计,底层设计信息和顶层架构设计共同组成了整个架构文档。底层设计细节和高层架构信…

Neo4j 国内镜像下载与安装

Neo4j 5.x 简体中文版指南 社区版:https://neo4j.com/download-center/#community 链接地址(Linux版):https://neo4j.com/artifact.php?nameneo4j-community-3.5.13-unix.tar.gz 链接地址(Windows)&#x…

如何使用react框架进行两个html页面的切换?

如何使用react框架进行两个html页面的切换? 项目背景首先是古老的做法login.htmlindex.html 正文->react框架如何设置两个页面的跳转?配置react框架的环境react框架如何实现两个页面的跳转? 项目背景 古老的html页面跳转的做法无法在react框架中直接适配,所以非常有必要…

MySQL-进阶-索引

一、索引概述 1、介绍 2、有误索引搜索效率演示 3、优缺点 二、索引结构 1、B-Tree(多路平衡查找树) 2、BTree 3、Hash 三、索引分类 四、索引语法 1、语法 2、案例 五、SQL性能分析 1、查看执行频次 2、慢查询日志 3、show-profile 4、explain 六、索…

redis 入门

一、什么是redis? redis是c语言编写的高性能(读的速度是110000次/s,写的速度是81000次/s)的k-v形式的数据库,数据存在内存中 二、redis的使用场景? 数据量小,访问量大 三、redis的启动和关闭 启动: 打开cmd&…

2. HarmonyOS应用开发DevEcoStudio准备-1

2. HarmonyOS应用开发DevEcoStudio准备-1 下载 DevEco Studio 进入HUAWEI DevEco Studio产品页产品页。 单击下载列表右侧的按钮,下载 DevEco Studio。 安装 DevEco Studio 下载完成后,双击下载的 deveco-studio-xxxx.exe,进入 DevEco St…

gitee建库并git

箴言:书山有路勤为径 文章目录 前言一、gitee导入ssh二、gitee建库三、克隆到本地四、关联本地工程到远程仓库五、push流程总结 前言 nodejs每天的学习都有代码产出,转念一想不如在码云上面搞个仓库,也经历了些许波折,往常也建了…

接口测试工具开发文档

1 开发规划 1.1 开发人员 角 色 主要职责 负责模块 人员 备注 n xxx模块 xxx 1.2 开发计划 <附开发计划表> 1.3 开发环境和工具 开发工具 工具 作用 Notepad 编辑器 Perl 解释器 2 总体设计 设计思路&#xff1a;因为测试app和server。首先必须…

LeetCode.11. 盛最多水的容器

题目 题目链接 分析 这道题的意思就是让我们找两个下标&#xff0c;以这两个下标组成的线为底&#xff0c;高度取这两个位置对应数字的最小值为高&#xff0c;组成一个长方形&#xff0c;求长方形最大的面积可以为多少。 暴力的解法是什么&#xff1f;&#xff1f;&#xf…

【Linux】开始使用 vim 吧!!!

Linux 1 what is vim &#xff1f;2 vim基本概念3 vim的基本操作 &#xff01;3.1 vim的快捷方式3.1.1 复制与粘贴3.1.2 撤销与剪切3.1.3 字符操作 3.2 vim的光标操作3.3 vim的文件操作 总结Thanks♪(&#xff65;ω&#xff65;)&#xff89;感谢阅读下一篇文章见&#xff01;…

工业4.0前沿:8DI/4DO/6AI RTU在石油管道监测中的应用

在当前数字化转型的大潮下&#xff0c;石油化工行业的智能化进程正以前所未有的速度推进。其中&#xff0c;物联网技术作为连接物理世界与数字世界的桥梁&#xff0c;在管道监控与安全管理领域发挥着至关重要的作用。一款专为石油化工管道设计的全网通物联网RTU终端应运而生&am…

消息中间件之RocketMQ(五)

RocketMQ高性能背后的核心原理 1.消息主从复制 如果Broker以一个集群的方式部署&#xff0c;会有一个master节点和多个Slave节点&#xff0c;消息需要从master复制到slave上&#xff0c;而消息复制的方式分为同步复制和异步复制。 同步复制: 同步复制是等Master和Slave都写入…

为什么网页打开慢?是服务器的问题吗?

当我们遇到网页加载缓慢时&#xff0c;首先想到的可能是服务器的问题。的确&#xff0c;服务器是影响网页加载速度的一个重要因素。然而&#xff0c;这并非是唯一的原因。实际上&#xff0c;网页加载速度受多种因素影响&#xff0c;包括但不限于服务器、网络带宽、DNS解析时间、…

linux0.11源码看信号的处理流程

序 日常Linux写代码或者使用中难免会使用siganl&#xff0c;包括我们使用ctrl-c结束程序&#xff0c;使用kill命令发送信号&#xff0c;或者说程序core后操作系统向程序发送的信号&#xff0c;以及我们程序内部自定义的信号处理。 我们选择linux0.11一个原因是它比较简单&…

程序员如何应对中年危机

中年危机是一个普遍存在的问题&#xff0c;不仅仅局限于程序员这个职业。不过&#xff0c;对于程序员来说&#xff0c;由于技术更新迅速&#xff0c;中年危机可能更加明显。以下是一些应对中年危机的建议&#xff1a; 持续学习新技术和工具&#xff1a;计算机技术发展迅速&…

快快销shop积分商城:全额积分抵扣营销 打造积分换购专区

快快销shop积分商城是一个创新的营销平台&#xff0c;它通过全额积分抵扣的策略&#xff0c;鼓励用户在商城内消费并积累积分。这种营销方式不仅能提升用户的购物体验&#xff0c;还能有效地促进销售。 全额积分抵扣意味着用户在商城内消费时&#xff0c;可以全额使用积分进行…

原生js是怎么创建元素的?

问: <div class"share-img"> <img src"../img/pic_share-tip.png" alt""> </div>原生js怎么创建一个这个元素? 回答: 问: 上面代码执行结果是什么样的? 回答:

攻防演练 |解决Nmap无法扫描B段资产问题

前段时间老大发来任务&#xff0c;让帮忙用nmap扫一些ip段&#xff0c;我拿过来就准备开扫… 但是发现nmap无法直接扫描同一B段不同C段下的IP段&#xff0c;例如111.111.111.0-111.111.222.255 原本我是准备写个工具联动nmap来扫描大批量IP段资产的 但是由于环境有些问题&am…

什么工具能将视频转成gif?分享一个在线制作gif网站

Gif动图看起来效果非常的炫酷&#xff0c;也很复杂。这种gif动图制作起来是不是也很麻烦呢&#xff1f;其实制作gif动画的方法非常的简单&#xff0c;不用下载软件&#xff0c;小白也能操作。只需要使用在线制作gif&#xff08;https://www.gif.cn/&#xff09;工具-GIF中文网&…

【云驻共创】零门槛Serverless课堂 应用全托管 so easy!

前言 一切要从一个风和日丽的早上说起&#xff1a; 那天&#xff0c;阳光正好&#xff0c;微风不燥。还来不及从容吃口早饭&#xff0c;我就接到了线上报警&#xff0c;赶忙打开了电脑&#xff0c;处理突发的流量高峰导致的页面报错。 重启好服务&#xff0c;饭都冷了。 我…