Go GetoptGo 解析命令行参数工具

联合创作 · 2023-10-02 05:41

Go GetOpt,让你在 go 里解析命令行参数无聊地跟写 shell 脚本一样。




为了不引起混淆,以下说明将使用


go getopt 表示本代码仓库


shell getoptgetopt 命令 表示 util-linux 中的 getopt 二进制程序


getopt(或 C getopt)表示 libc 中的 getopt 方法


但在某个上下文(如标题说明了该段是 shell getopt)中可能有时会直接使用 getopt 指代。请各位注意区分。




怎么用



go get gitee.com/go-getopt/go-getopt


package main

import (
"fmt"
"os"

// 这里为了方便,直接使用 . 进行 import。
// 这样可以直接调用 GetOpt、Get 和 Shift 方法。
. "gitee.com/go-getopt/go-getopt"
)

func main() {
// 传入 os.Args、options 和 longOptions 字符串参数即可
err := GetOpt(os.Args, "ab:c::", "a-long,b-long:,c-long::")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

// 解析后的参数列表存储在全局变量 Args 中
fmt.Println("Arguments:", Args)
fmt.Println("Program name:", Args[0])

// 接下来的步骤就和 shell 差不多了
for loop := true; loop; {
switch Get(1) {
case "-a", "--a-long":
fmt.Println("Option a")
Shift(1)
case "-b", "--b-long":
fmt.Println("Option b, argument '" + Get(2) + "'")
Shift(2)
case "-c", "--c-long":
if Get(2) == "" {
fmt.Println("Option c, no argument")
} else {
fmt.Println("Option c with arg '" + Get(2) + "'")
}
Shift(2)
case "--":
Shift(1)
loop = false
default:
fmt.Fprintln(os.Stderr, "Error: wrong argument '"+arg1+"'")
os.Exit(1)
}
}
fmt.Println("Remains:", Args[1:])
}

对比一下 shell getopt 解析命令行的脚本:



# 检查 getopt 命令是否正常运行
getopt --test > /dev/null

[ $? -ne 4 ] &&
echo "Error: command 'getopt --test' failed in this environment." &&
exit 1

# 设定 options 和 longOptions,调用 getopt 命令
options=ab:c::
longOptions=a-long,b-long:,c-long::

parsed=$(getopt --options=$options --longoptions=$longOptions --name "$0" -- "$@")

[ $? -ne 0 ] &&
echo "Error: failed to parse cmd arguments" &&
exit 1

eval "set -- $parsed"

# 循环判断是哪个 flag,处理完后 shift 掉
while true; do
case "$1" in
-a|--a-long)
echo 'Option a'
shift
;;
-b|--b-long)
echo "Option b, argument '$2'"
shift 2
;;
-c|--c-long)
[ -n "$2" ] && \\
echo "Option c, argument '$2'" || \\
echo 'Option c, no argument'
shift 2
--)
shift
break
*)
echo "Error: wrong argument '$1'"
exit 1
;;
esac
done
echo "Remains: $@"

Go GetOpt 适合哪些人用


如果你符合以下的一条或多条,可能这个库会适合你:



  • 想用一个库让 go 程序解析命令行参数方便一点。

  • 只想解析出字符串形式参数,然后自己做处理或转换。

  • 不想让类型断言、类型转换代码到处乱飞,也不需要调用的库提供 GetIntMustGetInt 之类的方法。

  • 忘不了前任 习惯了写 shell,想找个差不多的库接盘

  • 不喜欢 flagpflag 这种类型的解析方式(pflag 也很久没维护了)。

  • 不想用 cobra 这种很繁琐的库。


其他问题


并发(协程)安全吗?


没办法做到,也没必要。C 的 getoptgetopt_long 方法本身就不是并发安全的(用了全局变量 optindoptarg 来存储中间状态)。


而且,命令行应该只需要解析一次就可以了吧。有必要多次解析吗🤔?


支持哪些平台?


这个库是用 cgo 包装 libc 中的 getopt_long 方法实现的。原理和 shell getopt 命令行程序差不多。目前主流的 libc 都是支持的:



  • mingw / msvc Windows 系

  • glibc Debian 系、CentOS 系

  • musl Alpine

  • uclibc-ng BusyBox


附:各 libc getopt 和 getopt_long 源码地址


musl


https://git.musl-libc.org/cgit/musl/tree/src/misc/getopt.c


https://git.musl-libc.org/cgit/musl/tree/src/misc/getopt_long.c


glibc


https://sourceware.org/git/?p=glibc.git;a=blob;f=posix/getopt.c;h=e9661c79faa8920253bc37747b193d1bdcb288ef;hb=HEAD


https://sourceware.org/git/?p=glibc.git;a=blob;f=posix/getopt1.c;h=990eee1b64fe1ee03e8b22771d2e88d5bba3ac68;hb=HEAD


uclibc-ng


https://gogs.waldemar-brodkorb.de/oss/uclibc-ng/src/master/libc/unistd/getopt.c


https://gogs.waldemar-brodkorb.de/oss/uclibc-ng/src/master/libc/unistd/getopt_long-simple.c

浏览 36
点赞
评论
收藏
分享

手机扫一扫分享

编辑 分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

编辑 分享
举报