Skip to content

Commit 221a476

Browse files
committed
Allow checking whether optionals provided explicitly
Since boolean optionals default to "off" we have no way to check if a boolean optional was actually specified on the command line. The ability to check if a boolean optional has been given is useful in cases where we source values from a configuration file where we want any explicitly given arguments to override the configuration. An example that mostly works is (for plain optionals with an empty default): test -f ~/.standard-values-for-this-script && source "$_" MYOPT="${MYOPT:-$_arg_myopt}" ... # Code using MYOPT This works since optionals can default to an empty string. We still are not entirely sure if the optional was actually passed here, but it tends to be "good enough" in most cases. In the case where `myopt` is boolean, the above case would always use "off" if the argument was not specified, thus overriding the sourced configuration file. This patch introduces `_supplied_<argname>`, a value that gets set to 1 if an argument is supplied for that option, otherwise it's set to 0. This must be enabled on a per-argument basis by declaring: ARGBASH_INDICATE_SUPPLIED([long arg name]) Where `long arg name` matches the argument name for any `ARG_OPTIONAL_*`. This allows us to do the following: test -f ~/.standard-values-for-this-script && source "$_" if [ "$_supplied_arg_myopt" = 1 ]; then MYOPT="$_arg_myopt" # We override fi ... # Code using MYOPT
1 parent 5b0d2fd commit 221a476

File tree

7 files changed

+47
-2
lines changed

7 files changed

+47
-2
lines changed

bin/argbash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ then
495495
# match against suspicious, then inverse match against correct stuff:
496496
# #<optional whitespace>\(allowed\|another allowed\|...\)<optional whitespace><opening bracket <or> end of line>
497497
# Then, extract all matches (assumed to be alnum chars + '_') from grep and put them in the error msg.
498-
grep_output="$(printf "%s" "$output" | grep '^#\s*\(ARG_\|ARGBASH\)' | grep -v '^#\s*\(ARGBASH_SET_INDENT\|ARG_OPTIONAL_SINGLE\|ARG_VERSION\|ARG_VERSION_AUTO\|ARG_HELP\|ARG_OPTIONAL_INCREMENTAL\|ARG_OPTIONAL_REPEATED\|ARG_VERBOSE\|ARG_OPTIONAL_BOOLEAN\|ARG_OPTIONAL_ACTION\|ARG_POSITIONAL_SINGLE\|ARG_POSITIONAL_INF\|ARG_POSITIONAL_MULTI\|ARG_POSITIONAL_DOUBLEDASH\|ARG_OPTION_STACKING\|ARG_RESTRICT_VALUES\|ARG_DEFAULTS_POS\|ARG_LEFTOVERS\|ARGBASH_WRAP\|INCLUDE_PARSING_CODE\|DEFINE_LOAD_LIBRARY\|DEFINE_SCRIPT_DIR\|DEFINE_SCRIPT_DIR_GNU\|ARGBASH_SET_DELIM\|ARGBASH_GO\|ARGBASH_PREPARE\|ARG_TYPE_GROUP\|ARG_TYPE_GROUP_SET\|ARG_USE_ENV\|ARG_USE_PROGRAM\)\s*\((\|$\)' | sed -e 's/#\s*\([[:alnum:]_]*\).*/\1 /' | tr -d '\n\r')"
498+
grep_output="$(printf "%s" "$output" | grep '^#\s*\(ARG_\|ARGBASH\)' | grep -v '^#\s*\(ARGBASH_SET_INDENT\|ARG_OPTIONAL_SINGLE\|ARG_VERSION\|ARG_VERSION_AUTO\|ARG_HELP\|ARG_OPTIONAL_INCREMENTAL\|ARG_OPTIONAL_REPEATED\|ARG_VERBOSE\|ARG_OPTIONAL_BOOLEAN\|ARG_OPTIONAL_ACTION\|ARG_POSITIONAL_SINGLE\|ARG_POSITIONAL_INF\|ARG_POSITIONAL_MULTI\|ARG_POSITIONAL_DOUBLEDASH\|ARGBASH_INDICATE_SUPPLIED\|ARG_OPTION_STACKING\|ARG_RESTRICT_VALUES\|ARG_DEFAULTS_POS\|ARG_LEFTOVERS\|ARGBASH_WRAP\|INCLUDE_PARSING_CODE\|DEFINE_LOAD_LIBRARY\|DEFINE_SCRIPT_DIR\|DEFINE_SCRIPT_DIR_GNU\|ARGBASH_SET_DELIM\|ARGBASH_GO\|ARGBASH_PREPARE\|ARG_TYPE_GROUP\|ARG_TYPE_GROUP_SET\|ARG_USE_ENV\|ARG_USE_PROGRAM\)\s*\((\|$\)' | sed -e 's/#\s*\([[:alnum:]_]*\).*/\1 /' | tr -d '\n\r')"
499499
test -n "$grep_output" && die "Your script contains possible misspelled Argbash macros: $grep_output" 1
500500
fi
501501
if test "$outfname" != '-'

doc/guide.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,15 @@ Special arguments
305305
A use case for this is wrapping of scripts that are completely ``Argbash``-agnostic.
306306
Therefore, your script can take its own arguments and the rest that is not recognized can go to the wrapped script.
307307

308+
* Detect supplied arguments:
309+
::
310+
311+
ARGBASH_INDICATE_SUPPLIED([long opt arg name], [another long opt arg name (optional)], ...)
312+
313+
This macro takes a list of long optional argument names and will generate a variable for each optional that will be set if that argument was explicitly provided on the command line. This only works for optional arguments.
314+
315+
For example, if you have `ARG_OPTIONAL_BOOLEAN([quiet], , , [off])`, followed by `ARGBASH_INDICATE_SUPPLIED([quiet])`, then if `--quiet` was provided on the command line the variable `_supplied_arg_quiet=1` would be set. This allows you to see if an argument was explicitly provided using `[ "$_supplied_arg_quiet" = 1 ]`. If the argument was not passed to the program then this variable will be set to `0`.
316+
308317
Typing macros
309318
+++++++++++++
310319

src/collectors.m4

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,12 @@ m4_define([_ARG_POSITIONAL_DOUBLEDASH], [m4_do(
525525
)])
526526

527527

528+
argbash_api([ARGBASH_INDICATE_SUPPLIED], [m4_do(
529+
[[$0($@)]],
530+
[m4_set_add_all([HAVE_SUPPLIED], $@)],
531+
)])
532+
533+
528534
dnl
529535
dnl $1: The mode of argument grouping: One of 'none', 'getopts'
530536
argbash_api([ARG_OPTION_STACKING], _CHECK_PASSED_ARGS_COUNT(1)[m4_do(

src/stuff.m4

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ dnl $5: Where to get the last value (optional)
444444
m4_define([_VAL_OPT_ADD_SPACE_WITHOUT_GETOPT_OR_SHORT_OPT], [_JOIN_INDENTED(_INDENT_LEVEL_IN_ARGV_CASE_BODY,
445445
[test $[]# -lt 2 && die "Missing value for the optional argument '$_key'." 1],
446446
[$3([$1], ["@S|@2"], [$4])],
447+
[_INDICATION_OF_SUPPLY([$1], [$4])],
447448
[_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_SPACE([$4], [m4_default_quoted([$5], [@S|@2])])],
448449
[shift],
449450
)])
@@ -458,6 +459,7 @@ dnl $5: Where to get the last value (optional)
458459
m4_define([_VAL_OPT_ADD_GETOPTS], [_JOIN_INDENTED(_INDENT_LEVEL_IN_ARGV_CASE_BODY,
459460
[test "$OPTARG" = "" && die "Missing value for the optional argument '-$_key'." 1],
460461
[$3([$1], ["$OPTARG"], [$4])],
462+
[_INDICATION_OF_SUPPLY([$1], [$3])],
461463
[_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_SPACE([$4], [m4_default_quoted([$5], [$OPTARG])])],
462464
)])
463465
@@ -470,6 +472,7 @@ dnl $4: The name of the argument-holding variable
470472
dnl $5: Where to get the last value (optional)
471473
m4_define([_VAL_OPT_ADD_EQUALS_WITH_LONG_OPT], [_JOIN_INDENTED(_INDENT_LEVEL_IN_ARGV_CASE_BODY,
472474
[$3([$1], ["${_key##--$1=}"], [$4])],
475+
[_INDICATION_OF_SUPPLY([$1], [$4])],
473476
[_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_EQUALS([$4])],
474477
)])
475478
@@ -482,6 +485,7 @@ dnl $4: The name of the argument-holding variable
482485
dnl $5: Where to get the last value (optional)
483486
m4_define([_VAL_OPT_ADD_ONLY_WITH_SHORT_OPT_GETOPT], [_JOIN_INDENTED(_INDENT_LEVEL_IN_ARGV_CASE_BODY,
484487
[$3([$1], ["${_key##-$2}"], [$4])],
488+
[_INDICATION_OF_SUPPLY([$1], [$4])],
485489
[_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_GETOPT([$4])],
486490
)])
487491
@@ -522,6 +526,13 @@ m4_define([_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_EQUALS], [m4_do(
522526
)])
523527
524528
529+
dnl
530+
dnl $1: The name of the option arg
531+
dnl $2: The name of the argument-holding variable
532+
m4_define([_INDICATION_OF_SUPPLY], [m4_do(
533+
[m4_set_contains([HAVE_SUPPLIED], [$1], [_supplied$2=1], [])],
534+
)])
535+
525536
m4_define([_MAKE_SEE_ALSO_OPTION_PHRASE], [m4_do(
526537
[[See the comment of option '$1' to see what's going on here - principle is the same.]],
527538
)])
@@ -731,6 +742,7 @@ m4_define([_MAKE_OPTARG_SIMPLE_CASE_SECTION], [m4_do(
731742
[bool],
732743
[_JOIN_INDENTED(_INDENT_LEVEL_IN_ARGV_CASE_BODY,
733744
[[$5="on"]],
745+
[_INDICATION_OF_SUPPLY([$1], [$5])],
734746
[_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_SPACE([$5])],
735747
[[test "${1:0:5}" = "--no-" && $5="off"]],
736748
)],
@@ -763,6 +775,7 @@ m4_define([_MAKE_OPTARG_GETOPTS_CASE_SECTION], [m4_do(
763775
[bool],
764776
[_JOIN_INDENTED(_INDENT_LEVEL_IN_ARGV_CASE_BODY,
765777
[[$5="on"]],
778+
[_INDICATION_OF_SUPPLY([$1], [$5])],
766779
[_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_SPACE([$5])],
767780
)],
768781
[incr],
@@ -823,6 +836,7 @@ m4_define([_MAKE_OPTARG_GETOPT_CASE_SECTION], [m4_do(
823836
[bool],
824837
[_JOIN_INDENTED(_INDENT_LEVEL_IN_ARGV_CASE_BODY,
825838
[[$5="on"]],
839+
[_INDICATION_OF_SUPPLY([$1], [$5])],
826840
_PASS_WHEN_GETOPT([$2]),
827841
[_APPEND_WRAPPED_ARGUMENT_TO_ARRAY_SPACE([$5])],
828842
)],
@@ -1234,6 +1248,7 @@ m4_define([_MAKE_DEFAULTS_OPTIONAL], [m4_do(
12341248
[incr], [_arg_varname=m4_expand(_default)_ENDL_],
12351249
[repeated], [_arg_varname=(_default)_ENDL_],
12361250
[_arg_varname=_sh_quote(_default)_ENDL_])],
1251+
[m4_set_contains([HAVE_SUPPLIED], _argname, [[_supplied]_arg_varname=0]_ENDL_, [])],
12371252
)])],
12381253
)])
12391254

tests/regressiontests/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ test-simple: $(TESTDIR)/test-simple.sh
372372
ERROR="[Nn]ot enough" $(REVERSE) $<
373373
ERROR="require exactly 1" $(REVERSE) $<
374374
$< pos -o 'uf ta' | grep -q 'OPT_S=uf ta,POS_S=pos,'
375+
$< pos -o 'uf ta' --print-optionals | grep -q '_supplied_arg_prefix=1,_supplied_arg_print_optionals=1,_supplied_arg_la=0,_supplied_arg_not_supplied=x'
376+
$< pos -o 'uf ta' --print-optionals --la x | grep -q '_supplied_arg_prefix=1,_supplied_arg_print_optionals=1,_supplied_arg_la=1,_supplied_arg_not_supplied=x'
377+
$< pos -o 'uf ta' --print-optionals --la x --not-supplied | grep -q '_supplied_arg_prefix=1,_supplied_arg_print_optionals=1,_supplied_arg_la=1,_supplied_arg_not_supplied=x'
375378
test -z "$(SHELLCHECK)" || $(SHELLCHECK) "$(TESTDIR)/test-simple.sh"
376379
test-simple-dash: $(TESTDIR)/test-simple-dash.sh
377380
$< pos | grep -q 'OPT_S=x,POS_S=pos,'

tests/regressiontests/make/tests/tests-base.m4

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ m4_define([test_simple_body], [[
144144

145145
ADD_TEST_BASH([test-simple], [test_simple_body
146146
$< pos -o 'uf ta' | grep -q 'OPT_S=uf ta,POS_S=pos,'
147+
$< pos -o 'uf ta' --print-optionals | grep -q '_supplied_arg_prefix=1,_supplied_arg_print_optionals=1,_supplied_arg_la=0,_supplied_arg_not_supplied=x'
148+
$< pos -o 'uf ta' --print-optionals --la x | grep -q '_supplied_arg_prefix=1,_supplied_arg_print_optionals=1,_supplied_arg_la=1,_supplied_arg_not_supplied=x'
149+
$< pos -o 'uf ta' --print-optionals --la x --not-supplied | grep -q '_supplied_arg_prefix=1,_supplied_arg_print_optionals=1,_supplied_arg_la=1,_supplied_arg_not_supplied=x'
147150
])
148151

149152
ADD_TEST_DASH([test-simple], [test_simple_body

tests/regressiontests/test-simple.m4

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
# ARG_POSITIONAL_SINGLE([pos-arg], [help line PEND-\n-PBEGIN])
44
# ARG_OPTIONAL_SINGLE([prefix],[o],[help line END-\n-BEGIN "line 2" END-\\n-2BEGIN],[x])
5+
# ARG_OPTIONAL_BOOLEAN([print-optionals],[p],[Print the set of optional arguments],[off])
56
# ARG_OPTIONAL_SINGLE([la], [l], [help line END-\n-BEGIN "line 2"])
7+
# ARG_OPTIONAL_BOOLEAN([not-supplied],[s])
8+
# ARGBASH_INDICATE_SUPPLIED([prefix])
9+
# ARGBASH_INDICATE_SUPPLIED([print-optionals],[la])
610
# ARG_VERSION([echo "$0 FOO"])
711
# ARG_HELP([Testing program m4_fatal(BOOM!)], [m4_fatal([CRASH!])])
812
# ARG_DEFAULTS_POS()
@@ -12,7 +16,12 @@
1216

1317
# Now we take the parsed data and assign them no nice-looking variable names,
1418
# sometimes after a basic validation
15-
echo "OPT_S=$_arg_prefix,POS_S=$_arg_pos_arg,LA=$_arg_la,"
19+
if [ "$_arg_print_optionals" = on ]; then
20+
set -u
21+
echo "_supplied_arg_prefix=${_supplied_arg_prefix},_supplied_arg_print_optionals=${_supplied_arg_print_optionals},_supplied_arg_la=${_supplied_arg_la},_supplied_arg_not_supplied=${_supplied_arg_not_supplied-x}"
22+
else
23+
echo "OPT_S=$_arg_prefix,POS_S=$_arg_pos_arg,LA=$_arg_la,"
24+
fi
1625

1726
# ] <-- needed because of Argbash
1827
m4_ifdef([m4_esyscmd], [m4_fatal([The m4_esyscmd macro is enabled!])])

0 commit comments

Comments
 (0)