args.sh
· 6.4 KiB · Bash
Raw
#!/usr/bin/env bash
# Argument parser for bash scripts
#
# Author: Anthony Axenov (Антон Аксенов)
# Version: 1.6
# License: MIT
# Description: https://git.axenov.dev/anthony/shell/src/branch/master/helpers/arg-parser
#purpose Little helper to check if string matches PCRE
#argument $1 - some string
#argument $2 - regex
#exitcode 0 - string valid
#exitcode 1 - string is not valid
grep_match() {
printf "%s" "$1" | grep -qE "$2" >/dev/null
}
#purpose Find short argument or its value
#argument $1 - (string) argument (without leading dashes; only first letter will be processed)
#argument $2 - (number) is it flag? 1 if is, otherwise 0 or nothing
#argument $3 - (string) variable to return value into
# (if not specified then it will be echo'ed in stdout)
#returns (string) 1 (if $2 == 1), value (if correct and if $2 != 1) or nothing
#usage To get value into var: arg v 0 myvar or myvalue=$(arg 'v')
#usage To find flag into var: arg f 1 myvar or flag=$(arg 'f')
#usage To echo value: arg v
#usage To echo 1 if flag exists: arg f
arg() {
[ "$1" ] || { echo "Argument name is not specified!" >&2 && exit 1; }
local arg_name="${1:0:1}" # first character of argument name to find
local is_flag="$2" || 0 # 1 if we need just find a flag, 0 to get a value
local var_name="$3" || 0 # variable name to return value into or 0 to echo it in stdout
local value= # initialize empty value to check if we found one later
local arg_found=0 # marker of found argument
for idx in "${!__RAW_ARGS__[@]}"; do # going through all args
local arg_search=${__RAW_ARGS__[idx]} # get current argument
# skip $arg_search if it starts with '--' or letter
grep_match "$arg_search" "^(\w|--)" && continue
# clear $arg_search from special and duplicate characters, e.g. 'fas-)dfs' will become 'fasd'
local arg_chars="$(printf "%s" "$arg_search" \
| tr -s "[$arg_search]" 2>/dev/null \
| tr -d "[:punct:][:blank:]" 2>/dev/null)"
# if $arg_name is not one of $arg_chars the skip it
grep_match "-$arg_name" "^-[$arg_chars]$" || continue
arg_found=1
# then return '1'|'0' back into $value if we need flag or next arg value otherwise
[ "$is_flag" = 1 ] && value=1 || value="${__RAW_ARGS__[idx+1]}"
break
done
[ "$is_flag" = 1 ] && [ -z "$value" ] && value=0;
# if value we found is empty or looks like another argument then exit with error message
if [ "$arg_found" = 1 ] && ! grep_match "$value" "^[[:graph:]]+$" || grep_match "$value" "^--?\w+$"; then
echo "ERROR: Argument '-$arg_name' must have correct value!" >&2 && exit 1
fi
# return '$value' back into $var_name (if exists) or echo in stdout
[ "$var_name" ] && eval "$var_name='$value'" || echo "$value"
}
#purpose Find long argument or its value
#argument $1 - argument (without leading dashes)
#argument $2 - (number) is it flag? 1 if is, otherwise 0 or nothing
#argument $3 - (string) variable to return value into
# (if not specified then it will be echo'ed in stdout)
#returns (string) 1 (if $2 == 1), value (if correct and if $2 != 1) or nothing
#usage To get value into var: arg v 0 myvar or myvalue=$(arg 'v')
#usage To find flag into var: arg f 1 myvar or flag=$(arg 'f')
#usage To echo value: arg v
#usage To echo 1 if flag exists: arg f
argl() {
[ "$1" ] || { echo "Argument name is not specified!" >&2 && exit 1; }
local arg_name="$1" # argument name to find
local is_flag="$2" || 0 # 1 if we need just find a flag, 0 to get a value
local var_name="$3" || 0 # variable name to return value into or 0 to echo it in stdout
local value= # initialize empty value to check if we found one later
local arg_found=0 # marker of found argument
for idx in "${!__RAW_ARGS__[@]}"; do # going through all args
local arg_search="${__RAW_ARGS__[idx]}" # get current argument
if [ "$arg_search" = "--$arg_name" ]; then # if current arg begins with two dashes
# then return '1' back into $value if we need flag or next arg value otherwise
[ "$is_flag" = 1 ] && value=1 || value="${__RAW_ARGS__[idx+1]}"
break # stop the loop
elif grep_match "$arg_search" "^--$arg_name=.+$"; then # check if $arg like '--foo=bar'
# then return '1' back into $value if we need flag or part from '=' to arg's end as value otherwise
[ "$is_flag" = 1 ] && value=1 || value="${arg_search#*=}"
break # stop the loop
fi
done
[ "$is_flag" = 1 ] && [ -z "$value" ] && value=0;
# if value we found is empty or looks like another argument then exit with error message
if [ "$arg_found" = 1 ] && ! grep_match "$value" "^[[:graph:]]+$" || grep_match "$value" "^--?\w+$"; then
echo "ERROR: Argument '--$arg_name' must have correct value!" >&2 && exit 1;
fi
# return '$value' back into $var_name (if exists) or echo in stdout
[ "$var_name" ] && eval "$var_name='$value'" || echo "$value"
}
################################
# This is simple examples which you can play around with.
# 1. uncomment code below
# 2. call thi sscript to see what happens:
# /args.sh -abcd --flag1 --flag2 -e evalue -f fvalue --arg1=value1 --arg2 value2
# __RAW_ARGS__=("$@")
# arg a 1 flag_a
# echo "Flag -a has value '$flag_a'"
# echo "Flag -a has value '$(arg a 1)'"
# arg b 1 flag_b
# echo "Flag -b has value '$flag_b'"
# echo "Flag -b has value '$(arg b 1)'"
# arg c 1 flag_c
# echo "Flag -c has value '$flag_c'"
# echo "Flag -c has value '$(arg c 1)'"
# arg d 1 flag_d
# echo "Flag -d has value '$flag_d'"
# echo "Flag -d has value '$(arg d 1)'"
# argl flag1 1 flag_1
# echo "Flag --flag1 has value '$flag_1'"
# echo "Flag --flag1 has value '$(argl flag1 1)'"
# argl flag2 1 flag_2
# echo "Flag --flag2 has value '$flag_2'"
# echo "Flag --flag2 has value '$(argl flag2 1)'"
# arg e 0 arg_e
# echo "Arg -e has value '$arg_e'"
# echo "Arg -e has value '$(arg e 0)'"
# arg f 0 arg_f
# echo "Arg -f has value '$arg_f'"
# echo "Arg -f has value '$(arg f 0)'"
# argl arg1 0 arg_1
# echo "Arg --arg1 has value '$arg_1'"
# echo "Arg --arg1 has value '$(argl arg1 0)'"
# argl arg2 0 arg_2
# echo "Arg --arg2 has value '$arg_2'"
# echo "Arg --arg2 has value '$(argl arg2 0)'"
1 | #!/usr/bin/env bash |
2 | |
3 | # Argument parser for bash scripts |
4 | # |
5 | # Author: Anthony Axenov (Антон Аксенов) |
6 | # Version: 1.6 |
7 | # License: MIT |
8 | # Description: https://git.axenov.dev/anthony/shell/src/branch/master/helpers/arg-parser |
9 | |
10 | #purpose Little helper to check if string matches PCRE |
11 | #argument $1 - some string |
12 | #argument $2 - regex |
13 | #exitcode 0 - string valid |
14 | #exitcode 1 - string is not valid |
15 | grep_match() { |
16 | printf "%s" "$1" | grep -qE "$2" >/dev/null |
17 | } |
18 | |
19 | #purpose Find short argument or its value |
20 | #argument $1 - (string) argument (without leading dashes; only first letter will be processed) |
21 | #argument $2 - (number) is it flag? 1 if is, otherwise 0 or nothing |
22 | #argument $3 - (string) variable to return value into |
23 | # (if not specified then it will be echo'ed in stdout) |
24 | #returns (string) 1 (if $2 == 1), value (if correct and if $2 != 1) or nothing |
25 | #usage To get value into var: arg v 0 myvar or myvalue=$(arg 'v') |
26 | #usage To find flag into var: arg f 1 myvar or flag=$(arg 'f') |
27 | #usage To echo value: arg v |
28 | #usage To echo 1 if flag exists: arg f |
29 | arg() { |
30 | [ "$1" ] || { echo "Argument name is not specified!" >&2 && exit 1; } |
31 | local arg_name="${1:0:1}" # first character of argument name to find |
32 | local is_flag="$2" || 0 # 1 if we need just find a flag, 0 to get a value |
33 | local var_name="$3" || 0 # variable name to return value into or 0 to echo it in stdout |
34 | local value= # initialize empty value to check if we found one later |
35 | local arg_found=0 # marker of found argument |
36 | |
37 | for idx in "${!__RAW_ARGS__[@]}"; do # going through all args |
38 | local arg_search=${__RAW_ARGS__[idx]} # get current argument |
39 | |
40 | # skip $arg_search if it starts with '--' or letter |
41 | grep_match "$arg_search" "^(\w|--)" && continue |
42 | |
43 | # clear $arg_search from special and duplicate characters, e.g. 'fas-)dfs' will become 'fasd' |
44 | local arg_chars="$(printf "%s" "$arg_search" \ |
45 | | tr -s "[$arg_search]" 2>/dev/null \ |
46 | | tr -d "[:punct:][:blank:]" 2>/dev/null)" |
47 | |
48 | # if $arg_name is not one of $arg_chars the skip it |
49 | grep_match "-$arg_name" "^-[$arg_chars]$" || continue |
50 | arg_found=1 |
51 | |
52 | # then return '1'|'0' back into $value if we need flag or next arg value otherwise |
53 | [ "$is_flag" = 1 ] && value=1 || value="${__RAW_ARGS__[idx+1]}" |
54 | break |
55 | done |
56 | |
57 | [ "$is_flag" = 1 ] && [ -z "$value" ] && value=0; |
58 | |
59 | # if value we found is empty or looks like another argument then exit with error message |
60 | if [ "$arg_found" = 1 ] && ! grep_match "$value" "^[[:graph:]]+$" || grep_match "$value" "^--?\w+$"; then |
61 | echo "ERROR: Argument '-$arg_name' must have correct value!" >&2 && exit 1 |
62 | fi |
63 | |
64 | # return '$value' back into $var_name (if exists) or echo in stdout |
65 | [ "$var_name" ] && eval "$var_name='$value'" || echo "$value" |
66 | } |
67 | |
68 | #purpose Find long argument or its value |
69 | #argument $1 - argument (without leading dashes) |
70 | #argument $2 - (number) is it flag? 1 if is, otherwise 0 or nothing |
71 | #argument $3 - (string) variable to return value into |
72 | # (if not specified then it will be echo'ed in stdout) |
73 | #returns (string) 1 (if $2 == 1), value (if correct and if $2 != 1) or nothing |
74 | #usage To get value into var: arg v 0 myvar or myvalue=$(arg 'v') |
75 | #usage To find flag into var: arg f 1 myvar or flag=$(arg 'f') |
76 | #usage To echo value: arg v |
77 | #usage To echo 1 if flag exists: arg f |
78 | argl() { |
79 | [ "$1" ] || { echo "Argument name is not specified!" >&2 && exit 1; } |
80 | local arg_name="$1" # argument name to find |
81 | local is_flag="$2" || 0 # 1 if we need just find a flag, 0 to get a value |
82 | local var_name="$3" || 0 # variable name to return value into or 0 to echo it in stdout |
83 | local value= # initialize empty value to check if we found one later |
84 | local arg_found=0 # marker of found argument |
85 | |
86 | for idx in "${!__RAW_ARGS__[@]}"; do # going through all args |
87 | local arg_search="${__RAW_ARGS__[idx]}" # get current argument |
88 | |
89 | if [ "$arg_search" = "--$arg_name" ]; then # if current arg begins with two dashes |
90 | # then return '1' back into $value if we need flag or next arg value otherwise |
91 | [ "$is_flag" = 1 ] && value=1 || value="${__RAW_ARGS__[idx+1]}" |
92 | break # stop the loop |
93 | elif grep_match "$arg_search" "^--$arg_name=.+$"; then # check if $arg like '--foo=bar' |
94 | # then return '1' back into $value if we need flag or part from '=' to arg's end as value otherwise |
95 | [ "$is_flag" = 1 ] && value=1 || value="${arg_search#*=}" |
96 | break # stop the loop |
97 | fi |
98 | done |
99 | |
100 | [ "$is_flag" = 1 ] && [ -z "$value" ] && value=0; |
101 | |
102 | # if value we found is empty or looks like another argument then exit with error message |
103 | if [ "$arg_found" = 1 ] && ! grep_match "$value" "^[[:graph:]]+$" || grep_match "$value" "^--?\w+$"; then |
104 | echo "ERROR: Argument '--$arg_name' must have correct value!" >&2 && exit 1; |
105 | fi |
106 | |
107 | # return '$value' back into $var_name (if exists) or echo in stdout |
108 | [ "$var_name" ] && eval "$var_name='$value'" || echo "$value" |
109 | } |
110 | |
111 | ################################ |
112 | |
113 | # This is simple examples which you can play around with. |
114 | # 1. uncomment code below |
115 | # 2. call thi sscript to see what happens: |
116 | # /args.sh -abcd --flag1 --flag2 -e evalue -f fvalue --arg1=value1 --arg2 value2 |
117 | |
118 | # __RAW_ARGS__=("$@") |
119 | |
120 | # arg a 1 flag_a |
121 | # echo "Flag -a has value '$flag_a'" |
122 | # echo "Flag -a has value '$(arg a 1)'" |
123 | |
124 | # arg b 1 flag_b |
125 | # echo "Flag -b has value '$flag_b'" |
126 | # echo "Flag -b has value '$(arg b 1)'" |
127 | |
128 | # arg c 1 flag_c |
129 | # echo "Flag -c has value '$flag_c'" |
130 | # echo "Flag -c has value '$(arg c 1)'" |
131 | |
132 | # arg d 1 flag_d |
133 | # echo "Flag -d has value '$flag_d'" |
134 | # echo "Flag -d has value '$(arg d 1)'" |
135 | |
136 | # argl flag1 1 flag_1 |
137 | # echo "Flag --flag1 has value '$flag_1'" |
138 | # echo "Flag --flag1 has value '$(argl flag1 1)'" |
139 | |
140 | # argl flag2 1 flag_2 |
141 | # echo "Flag --flag2 has value '$flag_2'" |
142 | # echo "Flag --flag2 has value '$(argl flag2 1)'" |
143 | |
144 | # arg e 0 arg_e |
145 | # echo "Arg -e has value '$arg_e'" |
146 | # echo "Arg -e has value '$(arg e 0)'" |
147 | |
148 | # arg f 0 arg_f |
149 | # echo "Arg -f has value '$arg_f'" |
150 | # echo "Arg -f has value '$(arg f 0)'" |
151 | |
152 | # argl arg1 0 arg_1 |
153 | # echo "Arg --arg1 has value '$arg_1'" |
154 | # echo "Arg --arg1 has value '$(argl arg1 0)'" |
155 | |
156 | # argl arg2 0 arg_2 |
157 | # echo "Arg --arg2 has value '$arg_2'" |
158 | # echo "Arg --arg2 has value '$(argl arg2 0)'" |