Examples¶
Templates¶
Minimal example¶
Let’s call minimal example a script that accepts some arguments and prints their values.
Let’s consider a positional, optional, optional boolean, --version
and --help
arguments with parsing code embedded in the script.
First of all, we can generate the template using argbash-init
.
Then, we will edit it and add the script body.
First of all, we go examine argbash-init
help — either by running argbash-init -h
or looking into the documentation.
We find out that we can have argbash-init
generate the positional, optional arguments and help, so we go ahead:
bin/argbash-init --pos positional-arg --opt option --opt-bool print minimal.m4
The output of argbash-init
looks like this:
#!/bin/bash
# m4_ignore(
echo "This is just a script template, not the script (yet) - pass it to 'argbash' to fix this." >&2
exit 11 #)Created by argbash-init v2.10.0
# ARG_OPTIONAL_SINGLE([option])
# ARG_OPTIONAL_BOOLEAN([print])
# ARG_POSITIONAL_SINGLE([positional-arg])
# ARG_DEFAULTS_POS
# ARG_HELP([<The general help message of my script>])
# ARGBASH_GO
# [ <-- needed because of Argbash
# vvv PLACE YOUR CODE HERE vvv
# For example:
printf 'Value of --%s: %s\n' 'option' "$_arg_option"
printf "'%s' is %s\\n" 'print' "$_arg_print"
printf "Value of '%s': %s\\n" 'positional-arg' "$_arg_positional_arg"
# ^^^ TERMINATE YOUR CODE BEFORE THE BOTTOM ARGBASH MARKER ^^^
# ] <-- needed because of Argbash
We add useful information and the line with the --version
macro (by looking it up in the API docs) and the template finally looks better.
Plus, we append the actual script body to the template:
#!/bin/bash
# m4_ignore(
echo "This is just a script template, not the script (yet) - pass it to 'argbash' to fix this." >&2
exit 11 #)Created by argbash-init v2.10.0
# ARG_OPTIONAL_SINGLE([option], o, [A option with short and long flags and default], [boo])
# ARG_OPTIONAL_BOOLEAN([print], , [A boolean option with long flag (and implicit default: off)])
# ARG_POSITIONAL_SINGLE([positional-arg], [Positional arg description], )
# ARG_DEFAULTS_POS
# ARG_HELP([This is a minimal demo of Argbash potential])
# ARG_VERSION([echo $0 v0.1])
# ARGBASH_SET_INDENT([ ])
# ARGBASH_GO
# [ <-- needed because of Argbash
# vvv PLACE YOUR CODE HERE vvv
# For example:
if [ "$_arg_print" = on ]
then
echo "Positional arg value: '$_arg_positional_arg'"
echo "Optional arg '--option' value: '$_arg_option'"
else
echo "Not telling anything, print not requested"
fi
# ^^^ TERMINATE YOUR CODE BEFORE THE BOTTOM ARGBASH MARKER ^^^
# ] <-- needed because of Argbash
Here, we can notice multiple notable things:
argbash-init
has produced code that warn us if we treat the template as a script (i.e. if we execute it). This code will not be in the final script — it will disappear as we pass the template toargbash
.- Definitions of arguments are placed before the script body.
From
bash
point of view, they are commented out, so the “template” can be a syntactically valid script. - You access the values of argument
foo-bar
as$_arg_foo_bar
etc. (this is covered more in-depth in Using parsing results).
So let’s try the script in action!
We have to generate it first by passing the template to argbash
:
./../bin/argbash -o ../resources/examples/minimal.sh ../resources/examples/minimal.m4
This has produced the code we can observe below (notice that the leading “this is not a script error” lines have disappeared).
Let’s see what happens when we pass the -h
option:
resources/examples/minimal.sh -h
This is a minimal demo of Argbash potential
Usage: ../resources/examples/minimal.sh [-o|--option <arg>] [--(no-)print] [-h|--help] [-v|--version] <positional-arg>
<positional-arg>: Positional arg description
-o, --option: A option with short and long flags and default (default: 'boo')
--print, --no-print: A boolean option with long flag (and implicit default: off) (off by default)
-h, --help: Prints help
-v, --version: Prints version
OK, so it seems that passing it one (mandatory) positional arg will do the trick:
resources/examples/minimal.sh foo -o bar
Not telling anything, print not requested
Oops, we have forgot to turn print on! Let’s fix that…
resources/examples/minimal.sh foo -o bar --print
Positional arg value: 'foo'
Optional arg '--option' value: 'bar'
Separating the parsing code¶
Let’s take a look at a script that takes filename as the only positional argument and prints size of the corresponding file.
The caller can influence the unit of display using optional argument --unit
.
This script is a bit artificial, but hang on — we will try to use it from within a wrapping script.
This time, we will separate the parsing code and the script itself.
The parsing code will be in the simple-parsing.sh
file and the script then in simple.sh
.
Note
This is the manual approach.
A simpler way would be calling argbash-init
in the managed or decoupled mode — it will create the basic templates as in the previous example.
The template for the script’s parsing section is really simple.
Below are the sole contents of simple-parsing.m4
file:
#!/bin/bash
# ARG_POSITIONAL_SINGLE([filename])
# ARG_OPTIONAL_SINGLE([unit], u, [What unit we accept (b for bytes, k for kibibytes, M for mebibytes)], b)
# ARG_VERSION([echo $0 v0.1])
# ARG_OPTIONAL_BOOLEAN(verbose)
# ARG_HELP([This program tells you size of file that you pass to it in chosen units.])
# ARGBASH_SET_INDENT([ ])
# ARGBASH_GO
Then, let’s take a look at the script’s template body (i.e. the simple.m4
file):
#!/bin/bash
# DEFINE_SCRIPT_DIR()
# INCLUDE_PARSING_CODE([simple-parsing.sh])
# ARGBASH_GO
# [ <-- needed because of Argbash
# Now we take the parsed data and assign them no nice-looking variable names,
# sometimes after a basic validation
verbose=$_arg_verbose
unit=$_arg_unit
test -f $_arg_filename || { echo "Filename $_arg_filename doesn't seem to belong to a file"; exit 1; }
filename="$_arg_filename"
if [ $verbose = on ]
then
_b="bytes (B)"
_kb="kibibytes (kiB)"
_mb="mebibytes (MiB)"
else
_b="B"
_kb="kiB"
_mb="MiB"
fi
size_bytes=$(wc -c "$filename" | cut -f 1 -d ' ')
test "$unit" = b && echo $size_bytes $_b && exit 0
size_kibibytes=$(($size_bytes / 1024))
test "$unit" = k && echo $size_kibibytes $_kb && exit 0
size_mebibytes=$(($size_kibibytes / 1024))
test "$unit" = M && echo $size_mebibytes $_mb && exit 0
test "$verbose" = on && echo "The unit '$unit' is not supported"
exit 1
# ] <-- needed because of Argbash
We obtain the script from the template by running argbash
over it — it detects the parsing template and interconnects those two.
argbash simple.m4 -o simple.sh
In other words, it will examine the simple.m4
template, finding out that there is the INCLUDE_PARSING_CODE macro.
If the parsing template (in our case simple-parsing.m4
or simple-parsing.sh
) is found, a parsing script is produced out of it (otherwise, an error occurs).
Finally, the simple.sh
script is (re)generated — basically only the source directive is added, see those few lines:
#!/bin/bash
# DEFINE_SCRIPT_DIR([])
# INCLUDE_PARSING_CODE([simple-parsing.sh])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
# OTHER STUFF GENERATED BY Argbash
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" || { echo "Couldn't determine the script's running directory, which probably matters, bailing out" >&2; exit 2; }
. "$script_dir/simple-parsing.sh" # '.' means 'source'
### END OF CODE GENERATED BY Argbash (sortof) ### ])
When invoked with the help option, we get:
resources/examples/simple.sh -h
This program tells you size of file that you pass to it in chosen units.
Usage: ../resources/examples/simple.sh [-u|--unit <arg>] [-v|--version] [--(no-)verbose] [-h|--help] <filename>
-u, --unit: What unit we accept (b for bytes, k for kibibytes, M for mebibytes) (default: 'b')
-v, --version: Prints version
-h, --help: Prints help
It will work as long as the parsing code’s location (next to the script itself) doesn’t change:
Wrapping scripts¶
We will show how to write a script that accepts a list of directories and a glob pattern, combines them together, and displays size of files using the previous script.
In order to do this, we will introduce positional argument that can accept an arbitrary amount of values and we will also use the wrapping functionality that Argbash
possesses.
We want to wrap the simple.m4
(or simple.sh
).
However, since the script doesn’t include any command definitions, we have to wrap the parsing component simple-parsing.
.
The script’s template is still quite simple:
#!/bin/bash
# DEFINE_SCRIPT_DIR
# ARG_POSITIONAL_INF([directory], [Directories to go through], 1)
# ARG_OPTIONAL_SINGLE([glob], , [What files to match in the directory], [*])
# ARGBASH_WRAP([simple-parsing], [filename])
# ARG_HELP([This program tells you size of specified files in given directories in units you choose.])
# ARGBASH_SET_INDENT([ ])
# ARGBASH_GO
# [ <-- needed because of Argbash
script="$script_dir/simple.sh"
test -f "$script" || { echo "Missing the wrapped script, was expecting it next to me, in '$script_dir'."; exit 1; }
for directory in "${_arg_directory[@]}"
do
test -d "$directory" || die "We expected a directory, got '$directory', bailing out."
printf "Contents of '%s' matching '%s':\n" "$directory" "$_arg_glob"
for file in "$directory"/$_arg_glob
do
test -f "$file" && printf "\t%s: %s\n" "$(basename "$file")" "$("$script" "${_args_simple_parsing_opt[@]}" "$file")"
done
done
# ] <-- needed because of Argbash
The simple-parsing
in ARGBASH_WRAP argument refers to the parsing part of the script from the previous section.
Remember, we say that we are wrapping a script, but in fact, we just inherit a subset of its arguments and the actual wrapping (i.e. calling the wrapped script) is still up to us, although it is made easy by a great deal.
The filename
argument means that our wrapping script won’t “inherit” the filename
argument — that’s correct, it is the wrapping script that decides what arguments make it to the wrapped one.
When invoked with the help option, we get:
resources/examples/simple-wrapper.sh -h
This program tells you size of specified files in given directories in units you choose.
Usage: ../resources/examples/simple-wrapper.sh [--glob <arg>] [-u|--unit <arg>] [--(no-)verbose] [-h|--help] <directory-1> [<directory-2>] ... [<directory-n>] ...
<directory>: Directories to go through
--glob: What files to match in the directory (default: '*')
-u, --unit: What unit we accept (b for bytes, k for kibibytes, M for mebibytes) (default: 'b')
-h, --help: Prints help
So let’s try it!
resources/examples/simple-wrapper.sh --glob '*.m4' ../src ../resources/examples -u k
Contents of '../src' matching '*.m4':
argbash-1to2.m4: 1 kiB
argbash-init.m4: 6 kiB
argbash-lib.m4: 0 kiB
argbash.m4: 10 kiB
argument_value_types.m4: 5 kiB
collectors.m4: 21 kiB
constants.m4: 0 kiB
default_settings.m4: 0 kiB
docopt.m4: 3 kiB
env_vars.m4: 1 kiB
function_generators.m4: 7 kiB
list.m4: 6 kiB
output-bash-script.m4: 1 kiB
output-completion.m4: 4 kiB
output-docopt.m4: 0 kiB
output-manpage-defs.m4: 1 kiB
output-manpage.m4: 1 kiB
output-posix-script.m4: 3 kiB
output-strip-all.m4: 0 kiB
output-strip-none.m4: 0 kiB
output-strip-user-content.m4: 0 kiB
progs.m4: 2 kiB
stuff.m4: 47 kiB
utilities.m4: 11 kiB
value_validators.m4: 5 kiB
Contents of '../resources/examples' matching '*.m4':
minimal.m4: 0 kiB
minimal-raw.m4: 0 kiB
simple.m4: 0 kiB
simple-parsing.m4: 0 kiB
simple-standalone.m4: 0 kiB
simple-wrapper.m4: 0 kiB
Source¶
Minimal example¶
Let’s examine the generated minimal example script (the contents are displayed below).
We can see that the header still contains the Argbash
definitions.
They are not there for reference only, you can actually change them and re-run Argbash
on the script again to get an updated version!
Yes, you don’t need the .m4
template, the .sh
file serves as a template that is equally good!