An elegant option parser for shell scripts (full support for all POSIX shells)
getoptions is a new option parser and generator written in POSIX-compliant shell script and released in august 2020.It is for those who want to support the POSIX / GNU style option syntax in your shell scripts.Most easy, simple, fast, small, extensible and portable. No more any loops and templates needed!
#!/bin/sh
VERSION="0.1"
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..." ''
msg -- 'Options:'
flag FLAG -f --flag -- "takes no arguments"
param PARAM -p --param -- "takes one argument"
option OPTION -o --option on:"default" -- "takes one optional argument"
disp :usage -h --help
disp VERSION --version
}
eval "$(getoptions parser_definition) exit 1"
echo "FLAG: $FLAG, PARAM: $PARAM, OPTION: $OPTION"
printf '%s\n' "$@" # rest arguments
It generates a simple option parser code internally and parses the following arguments.
example.sh -f --flag -p VALUE --param VALUE -o --option -oVALUE --option=VALUE 1 2 3
$ example.sh --help
Usage: example.sh [options]... [arguments]...
Options:
-f, --flag takes no arguments
-p, --param PARAM takes one argument
-o, --option[=OPTION] takes one optional argument
-h, --help
--version
getopt
vs getopts
vs getoptions
getopt
nor getopts
is used, and implemented with shell scripts onlygetoptions
OPTARG
and OPTIND
)getopt
vs getopts
vs getoptions
getopt | getopts | getoptions | |
---|---|---|---|
Implementation | external command | shell builtin command | shell script |
Portability | No | Yes | Yes |
Short option beginning with - |
|
|
|
Short option beginning with + |
|
|
|
Combining short options |
|
|
|
Long option beginning with -- |
|
|
|
Long option beginning with - |
|
|
|
Abbreviating long options |
|
|
|
Optional argument |
|
|
|
Option after arguments |
|
|
|
Stop option parsing with -- |
|
|
|
Scanning modes |
|
|
+ and enhancement |
Subcommand |
|
|
|
Validation by pattern matching |
|
|
|
Custom validation |
|
|
|
Custom error handler |
|
|
|
Automatic help generation |
|
|
|
Almost no requirements.
dash
0.5.4+, bash
2.03+, ksh
88+, mksh
R28+, zsh
3.1.9+, yash
2.29+, busybox ash
1.1.3+, etccat
is used for help display, but it can be removedDownload prebuild shell scripts from releases.
wget https://github.com/ko1nksm/getoptions/releases/latest/download/getoptions -O $HOME/bin/getoptions
chmod +x $HOME/bin/getoptions
# optional
wget https://github.com/ko1nksm/getoptions/releases/latest/download/gengetoptions -O $HOME/bin/gengetoptions
chmod +x $HOME/bin/gengetoptions
Or build and install it yourself.
git clone https://github.com/ko1nksm/getoptions.git
cd getoptions
make
make install PREFIX=$HOME
brew tap ko1nksm/getoptions
brew install getoptions
Support three ways of use. It is better to use it as a command at first,and then use it as a library or generator as needed.
command | library | generator | |
---|---|---|---|
easy | ★★★ | ★★☆ | ★☆☆ |
fast | ★☆☆ | ★★☆ | ★★★ |
Use the getoptions
command that you installed on your system.This assumes that you have the getoptions
command installed,but it is the easiest to use and is suitable for personal scripts.
The execution speed is slightly slower than using it as a library. (Approx. 15ms overhead)
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..."
...
}
eval "$(getoptions parser_definition parse) exit 1"
parse "$@"
eval "set -- $REST"
The above code exit 1
is the recommended option.This allows you to exit if the getoptions
command is not found.
If you omit the option parser name or use -
, it will define the default optionparser and parse arguments immediately.
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..."
...
}
eval "$(getoptions parser_definition) exit 1"
# The above means the same as the following code.
# eval "$(getoptions parser_definition getoptions_parse) exit 1"
# getoptions_parse "$@"
# eval "set -- $REST"
HINT: Are you wondering why the external command can call a shell function?
The external command getoptions
will output the shell function getoptions
.The external command getoptions
will be hidden by the shell function getoptions
that defined by eval
,and the getoptions
will be called again, so it can be call the shell function parser_ definition
.
Try running the following command to see what is output.
$ getoptions parser_definition parse
The getoptions
command is not recommended for use in distribution scriptsbecause it is not always installed on the system. This problem can be solved byincluding getoptions as a shell script library in your shell scripts.
To use getoptions as a library, you need to generate a library using the gengetoptions
command.You can optionally adjust the indentation and other settings when generating the library.
$ gengetoptions library > getoptions.sh
. ./getoptions.sh # Or include it here
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..."
...
}
eval "$(getoptions parser_definition parse)"
parse "$@"
eval "set -- $REST"
NOTE for 1.x and 2.x users: The previous version guided you to use lib/*.sh
.This is still available, but it is recommended to use gengetoptions library
.
If you do not want to include getoptions in your shell scripts, you can pre-generate an option parser.It also runs the fastest, so it suitable when you need a lot of options.
$ gengetoptions parser -f examples/parser_definition.sh parser_definition parse prog > parser.sh
. ./parser.sh # Or include it here
parse "$@"
eval "set -- $REST"
You can use gengetoptions embed
to embed the generated code in a file,which makes maintenance easier.
If you want to write the parser definition in the same file asthe shell script to execute, define it between @getoptions
and @end
.The code contained here will be executed during code generation.
The generated code will be embedded between the @gengetoptions
and @end
directives.The arguments of @gengetoptions
are the same as the arguments of the gengetoptions
command,which allows you to embed the library as well as the parser.
Example
example.sh
#!/bin/sh
set -eu
# @getoptions
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..." ''
msg -- 'Options:'
flag FLAG -f --flag -- "takes no arguments"
param PARAM -p --param -- "takes one argument"
option OPTION -o --option on:"default" -- "takes one optional argument"
disp :usage -h --help
disp VERSION --version
}
# @end
# @gengetoptions parser -i parser_definition parse
#
# INSERTED HERE
#
# @end
parse "$@"
eval "set -- $REST"
echo "FLAG: $FLAG, PARAM: $PARAM, OPTION: $OPTION"
printf '%s\n' "$@" # rest arguments
$ gengetoptions embed --overwrite example.sh
Ubuntu (dash) Core i7 3.4 Ghz
[command]
Benchmark #1: ./example.sh --flag --param param --option=option a b c
Time (mean ± σ): 8.6 ms ± 0.3 ms [User: 6.3 ms, System: 0.6 ms]
Range (min … max): 7.7 ms … 10.1 ms 300 runs
[library]
Benchmark #1: ./example.sh --flag --param param --option=option a b c
Time (mean ± σ): 8.3 ms ± 0.4 ms [User: 5.2 ms, System: 0.5 ms]
Range (min … max): 7.4 ms … 10.5 ms 322 runs
[generator]
Benchmark #1: ./example.sh --flag --param param --option=option a b c
Time (mean ± σ): 4.7 ms ± 0.3 ms [User: 1.4 ms, System: 0.1 ms]
Range (min … max): 4.4 ms … 6.5 ms 510 runs
macOS (bash), Core i5 2.4 GHz
[command]
Benchmark #1: ./example.sh --flag --param param --option=option a b c
Time (mean ± σ): 37.6 ms ± 3.2 ms [User: 29.2 ms, System: 7.4 ms]
Range (min … max): 33.4 ms … 47.5 ms 66 runs
[library]
Benchmark #1: ./example.sh --flag --param param --option=option a b c
Time (mean ± σ): 31.0 ms ± 3.7 ms [User: 26.0 ms, System: 4.4 ms]
Range (min … max): 26.4 ms … 43.8 ms 77 runs
[generator]
Benchmark #1: ./example.sh --flag --param param --option=option a b c
Time (mean ± σ): 5.6 ms ± 2.1 ms [User: 2.8 ms, System: 2.0 ms]
Range (min … max): 3.9 ms … 15.1 ms 277 runs
It is important to know what kind of code is being generatedwhen the option parser is not working as expected.
If you want to see the the option parser code, rewrite it as follows.
# eval "$(getoptions parser_definition parse) exit 1"
# Preload the getoptions library
# (can be omitted when using getoptions as a library)
eval "$(getoptions -)"
# Output of the option parser
getoptions parser_definition parse
exit
The option parsing code generated by getoptions is very simple.
FLAG=''
PARAM=''
OPTION=''
REST=''
parse() {
OPTIND=$(($#+1))
while OPTARG= && [ $# -gt 0 ]; do
case $1 in
--?*=*) OPTARG=$1; shift
eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
;;
--no-*|--without-*) unset OPTARG ;;
-[po]?*) OPTARG=$1; shift
eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'}
;;
-[fh]?*) OPTARG=$1; shift
eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
OPTARG= ;;
esac
case $1 in
'-f'|'--flag')
[ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG=''
FLAG="$OPTARG"
;;
'-p'|'--param')
[ $# -le 1 ] && set "required" "$1" && break
OPTARG=$2
PARAM="$OPTARG"
shift ;;
'-o'|'--option')
set -- "$1" "$@"
[ ${OPTARG+x} ] && {
case $1 in --no-*|--without-*) set "noarg" "${1%%\=*}"; break; esac
[ "${OPTARG:-}" ] && { shift; OPTARG=$2; } || OPTARG='default'
} || OPTARG=''
OPTION="$OPTARG"
shift ;;
'-h'|'--help')
usage
exit 0 ;;
'--version')
echo "${VERSION}"
exit 0 ;;
--)
shift
while [ $# -gt 0 ]; do
REST="${REST} \"\${$(($OPTIND-$#))}\""
shift
done
break ;;
[-]?*) set "unknown" "$1"; break ;;
*)
REST="${REST} \"\${$(($OPTIND-$#))}\""
esac
shift
done
[ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
case $1 in
unknown) set "Unrecognized option: $2" "$@" ;;
noarg) set "Does not allow an argument: $2" "$@" ;;
required) set "Requires an argument: $2" "$@" ;;
pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
notcmd) set "Not a command: $2" "$@" ;;
*) set "Validation error ($1): $2" "$@"
esac
echo "$1" >&2
exit 1
}
usage() {
cat<<'GETOPTIONSHERE'
Usage: example.sh [options]... [arguments]...
Options:
-f, --flag takes no arguments
-p, --param PARAM takes one argument
-o, --option[=OPTION] takes one optional argument
-h, --help
--version
GETOPTIONSHERE
}
# Do not execute
The getoptions correctly handles arguments containing spaces and quoteswithout using arrays, which are not available in POSIX shells.
The magic is in the REST
variable in the following code.
$ examples.sh --flag 1 --param value 2 -- 3
# examples.sh
...
eval "$(getoptions parser_definition parse "$0") exit 1"
parse "$@"
eval "set -- $REST"
echo "$REST" # => "${2}" "${5}" "${7}"
echo "$@" # => 1 2 3
...
OPTARG
and OPTIND
for different purposes?This is to avoid using valuable global variables. The POSIX shell does not have local variables.Instead of using long variable names to avoid conflicts, we reuse OPTARG
and OPTIND
.This code has been tested to work without any problem with all POSIX shells (e.g. ksh88, bash 2.03).
If you use getoptions
instead of getopts
for option parsing, OPTARG
and OPTIND
are not needed.In addition, you can also use getopts
, since OPTARG
and OPTIND
will be correctly reset after use.
If you still don't like it, you can use the --optarg
and --optind
options of gengetoptions
to change the variable name.In addition, since the license of getoptions
is CC0, you can modify it to use it as you like.
The option parser code contains workarounds for some shell bugs.If you want to know what that code means, please refer to Workarounds.md.
For more information, see References.
When the getoptions
is used as an external command, three global functions,getoptions
, getoptions_help
, and getoptions_abbr
, are defined in your shell script.
If you are using it as a library, only getoptions
is required.The other functions are needed when the corresponding features are used.
Helper functions are (setup
, flag
, param
, etc) used to define option parsers,and are defined only within the global functions described above.
This is an example of basic usage. It should be enough for your personal script.
Shell scripts distributed as utilities may require advanced features and validation.
By defining the custom error handler, you can change the standard error messages,respond to additional error messages, and change the exit status.
By defining your own helper functions, you can easily define advanced options.For example, getoptions does not have a helper function to assign to the array,but it can be easily implemented by a custom helper function.
Complex programs are often implemented using subcommands.When using subcommands in getoptions, parse the arguments multiple times.(For example, parse up to the subcommand, and then parse after it.This design is useful for splitting shell scripts by each subcommand.
If you define a prehook
function in the parser definition,it will be calledbefore helper functions is called.This allows you to process the arguments before calling the helper function.
This feature was originally designed to handle variable names with prefixeswithout complicating getoptions. Therefore, it may not be very flexible.
NOTE: The prehook
function is not called in the help.
TODO:
extension.sh
Recall that the parser definition function is just a shell script.You can extend the functionality by calling it from your function.For example, you could add a required
attribute that means nonsense required options.
getoptions was originally developed to improve the maintainability and testability for ShellSpecwhich has number of options. ShellSpec optparser is another good example of how to use getoptions.
getoptions_help
is no longer needed (see help
attribute)default
attribute of the option
helper function to the on
attributelin/getoptions.sh
to lin/getoptions_base.sh
getoptions-cli
to gengetoptions
getoptions
to gengetoptions
=
and #
off
to no
@off
to @no
Tests are executed using shellspec.
# Install shellspec (if not installed)
curl -fsSL https://git.io/shellspec | sh
# Run tests
shellspec
# Run tests with other shell
shellspec --shell bash
NOTE: Currently, only the option parser is being tested,and the CLI utilities is not being tested.
Creative Commons Zero v1.0 Universal
All rights are relinquished and you can used as is or modified in your project.No credit is also required, but I would appreciate it if you could credit me as the original author.
环境: vue-cli: 5.0.4 vue: 3.2.13 webpack: 5.72 ts-loader: 9.x electron-builder运行报错: npm run electron:serve TypeError: loaderContext.getOptions is not a function 解决: 方式1: vue-cli 降到4.x版本 方式2:使用ts-loader
Vue项目中,遇到了Module build failed: TypeError: this.getOptions is not a function的问题 在我的项目中,我以为postcss没有用,然后卸载了,可是这个好像是被集成的,如果去掉这个地方就会报错,所以我在package.json中加上了postcss相关的插件,然后npm install就解决了。 "postcss-impo
原因: less-loader安装的版本过高 解决方案: 1.npm uninstall less-loader 2.npm install less-loader@5.0.0
该错误是css预编译的loader与webpack 4 不兼容导致的。尝试升级到webpack 5 或者降级sass-loader / less-loader / postcss-loader 的版本号到上一个主版本号。 比如: # 移除sass-loader@11 yarn remove sass-loader -D # 安装上一个主版本的sass-loader yarn add sass-l
报错的原因可能是 因为下载的 laoder-utils 的包不同 。我下载的是3.2.x 的包报错,你可以更换为 2.x 的loader- utils 的包 cnpm i loader-utils@2.0 -D
原因: scss版本不匹配,或者版本太高 如果安装的loader版本太高而安装的node-scss版本又太低就会出现不兼容从而报错的问题, 以下为有效匹配版本: sass-loader 4.1.1,node-sass 4.3.0 sass-loader 7.0.3,node-sass 4.7.2 sass-loader 7.3.1,node-sass 4.7.2 如果单独安装node-sass报错
在添加 less 样式后 安装了 yarn add less less-loader <style lang="less" scoped> .login_container{ background-color: #2b6b4b; } </style> 成功安装后 在package.json文件中找到配置项,如下 "less": "^4.1.2", "less-loade
npm uninstall --save sass-loader npm i -D sass-loader@8.x npm uninstall --save node-sass npm i node-sass@4.14.1 // 如果node-sass安装失败,也可以直接在package.json中写上版本号,删除node_modules文件夹,重新npm i即可
出现场景: 运行vue项目时报错 报错信息: Syntax Error: TypeError: this.getOptions is not a function @ ./node_modules/vue-style-loader??ref--8-oneOf-1-0!./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_m
使用css-loader@6.6.0依赖报错 一开始尝试把css-loader版本降低为5.0.0的一系列版本后项目还是无法正常跑起来。 于是降了个0.28.0的大版本后,报错消失。 "css-loader": "^0.28.0",
问题:Vue3脚手架引入less报错,TypeError: this.getOptions is not a function 原因:less-loader版本过高。 解决:降低less-loader版本至less-loader@7.3.0 先卸载当前版本 yarn remove less-loader 再重新安装低版本less-loader@7.3.0 yarn add less-loader
一问题: 最近在做一个老项目的迭代,发现老项目都用的是css,过于繁琐,就下载了sass.但是运行就报Error: Module build failed: TypeError: this.getOptions is not a function的错 二排查: 在网上搜索,有文章说是解决less相关问题的说是“ less-loader版本太高了,安装低版本的就没问题啦,比如5.0.0
在安装less 插件时,因less版本高,不兼容,要降低less版本即可 npm i less@3.9.0 less-loader@4.1.0 --save-dev
vue项目使用sass报错 报错信息如下: this.getOptions is not a function 原因是sass-loader版本太高 重新安装 yarn add sass-loader@7 -D yarn add sass -D