Last active
December 28, 2022 10:57
-
-
Save iwill/97935594437d560bbaaa20037a7b73bb to your computer and use it in GitHub Desktop.
A Bash Script - Get Options without `getopt`, `getopts` or `gnu-getopt`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
## basic | |
set -e -o pipefail | |
cmd="get-opts.sh" | |
RED='01;38;5;196' | |
LINK='04;38;5;4' | |
function color { | |
params=$1 | |
shift | |
echo -e "\033[${params}m$@\033[0m" | |
} | |
## prepare | |
cmd_debug=false | |
function debug { | |
if ! $cmd_debug; then | |
return 0 | |
fi | |
if [[ $# -eq 0 || "$1" == "-n" ]]; then | |
shift || true | |
echo | |
fi | |
if [[ $# -gt 0 ]]; then | |
echo -e "# $@" | |
fi | |
} | |
if [[ $1 = "d" || $1 == "debug" ]]; then | |
cmd_debug=true | |
debug -n "[$1]:" | |
debug " got cmd_debug [$cmd_debug]" | |
shift | |
fi | |
if [[ $# = 0 ]]; then | |
set -- "-h" | |
elif [[ "$1" = "-t" || "$1" = "--test" ]]; then | |
echo | |
cat << EOF | |
Test with builtin cases: | |
${cmd} -a --opt_b "" --opt_c="-x: \\\`\"'\\\\" d e - -no --unknown -- --ab -c -- d e | |
EOF | |
set -- -a --opt_b "" --opt_c="-x: \`\"'\\" d e - -no --unknown -- --ab -c -- d e | |
fi | |
## get opts | |
p_args=() # positional | |
next_arg_is_value=false | |
while [[ $# > 0 ]]; do | |
debug -n "[$1]:" | |
this_arg_is_value=$next_arg_is_value | |
next_arg_is_value=false | |
case "$1" in | |
# customized options | |
-a|--opt_a) | |
opt_a=true | |
shift | |
debug " got opt_a [$opt_a]" | |
;; | |
-b|--opt_b) | |
opt_b=true | |
debug " got opt_b [$opt_b]" | |
shift | |
if [[ "$1" != -* ]] || $this_arg_is_value; then | |
this_arg_is_value=true # for the `shift` at the end of `while` | |
arg_b=$1 | |
debug " got arg_b [$arg_b]" | |
fi | |
;; | |
-c|--opt_c) | |
opt_c=true | |
debug " got opt_c [$opt_c]" | |
shift | |
if [[ "$1" != -* ]] || $this_arg_is_value; then | |
this_arg_is_value=true # for the `shift` at the end of `while` | |
arg_c=$1 | |
debug " got arg_c [$arg_c]" | |
fi | |
;; | |
# stantard options | |
\?|-h|--help) | |
opt_help=true | |
debug " got opt [$opt_help]" | |
shift | |
;; | |
-*=*) # split to arg and value | |
arg="${1%=*}" | |
val="${1#*=}" | |
next_arg_is_value=true | |
debug " split to [$arg] and [$val]" | |
shift; set -- "$arg" "$val" "$@" | |
debug " set args" | |
;; | |
--) # after this arg, only positional arguments are accepted | |
shift; p_args+=("$@") | |
debug " got p_args after -- [$@]" | |
set -- | |
debug " set args" | |
;; | |
-|-?|--?*) # `-?`: 1 char at most, `--?*`: 1 char at least | |
>&2 echo -e "`color $RED unknown arg [$1]`" | |
shift | |
;; | |
-??*) # `-??*`: 2 chars at least, split combined options, e.g. `-ab` -> `-a` and `-b` | |
# remove first `-`, split to chars, prepand `-` to each char | |
opts=(`echo "${1##-}" | grep -o . | sed 's/^/-/'`) # https://stackoverflow.com/a/7579022/456536 | |
debug " split to opts [${opts[@]}]" | |
shift; set -- "${opts[@]}" "$@" | |
debug " set args" | |
;; | |
*) # positional arguments | |
p_args+=("$1") | |
debug " got p_arg [$1]" | |
shift | |
;; | |
esac | |
if $this_arg_is_value; then | |
this_arg_is_value=false | |
shift | |
fi | |
debug " left args [$@]" | |
done | |
set -- "${p_args[@]}" | |
if ! ${opt_help:-false}; then | |
echo | |
echo "Results:" | |
echo "# opt_a: [$opt_a]" | |
echo "# opt_b: [$opt_b], val: [$arg_b]" | |
echo "# opt_c: [$opt_c], val: [$arg_c]" | |
echo "# p_args: [$@]" | |
echo | |
exit 0 | |
fi | |
## help | |
less -r << EOF | |
`color $RED NAME` | |
`color $RED $cmd` - Get Options without \`getopt\`, \`getopts\` or \`gnu-getopt\`. | |
`color $RED SYNOPSIS` | |
`color $RED $cmd` [`color $RED debug`] [<args>] | |
`color $RED $cmd` [`color $RED debug`] `color $RED -t`|`color $RED --test` | |
`color $RED $cmd` `color $RED -h`|`color $RED --help` | |
`color $RED DESCRIPTION` | |
Get Options without \`getopt\`, \`getopts\` or \`gnu-getopt\`, which based on | |
Bruno Bronosky's answer: | |
`color $LINK https://stackoverflow.com/a/14203146/456536` | |
Commands: | |
`color $RED d`|`color $RED debug` Display debug information. | |
Options: | |
`color $RED -t`|`color $RED --test` Test with builtin cases. | |
`color $RED -h`|`color $RED --help` Display help information about $cmd. | |
Features: | |
short option | |
-a | |
long option | |
--opt_b | |
combined options | |
-no # split to \`-n -o\` | |
option with space separated value | |
--opt_b "value" | |
option with equal sign separated value | |
--opt_c="value" | |
empty string value | |
--opt_b "" | |
value with specific characters | |
--opt_c="-x: \\\`\"'\\\\" | |
unknown options | |
- -no --unknown | |
signify the end of the options | |
-- | |
positional arguments | |
d e\` and \`--ab -c -- d e | |
Customize: | |
no arg | |
if [[ \$# = 0 ]]; then ... | |
start with command | |
if [[ "$0" = "debug" ]]; then ... | |
`color $RED AUTHOR` | |
Written by Míng <`color $LINK [email protected]`>. Idea from Bruno Bronosky: | |
`color $LINK https://stackoverflow.com/users/117471/bruno-bronosky` | |
`color $RED COPYRIGHT` | |
Copyright (c) 2022 Míng. $cmd is available under the MIT license. | |
EOF | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Download
get-opts.sh
:Allow all users can run:
Display help information:
Test with builtin cases:
Result would be: