Context
I'm trying to write a decent bash parser using separate files and functions for:
- argument parsing (e.g. determining what to do)
- parsed argument processing (e.g. doing those things)
- printing the CLI usage.
However, when I try to pass the "POSITIONAL_ARGS" into the parse_args() {
, the arguments don't seem to arrive.
Example:
Suppose I have the following main.sh
file in directory: /src
:
#!/bin/bash
POSITIONAL_ARGS=()
# source arg_parser.sh
source src/arg_parser/arg_parser.sh
source src/arg_parser/print_usage.sh
# print the usage if no arguments are given
[ $# -eq 0 ] && { print_usage; exit 1; }
echo "POSITIONAL_ARGS=$POSITIONAL_ARGS"
parse_args "$POSITIONAL_ARGS"
And the following arg_parser.sh
file in: src/arg_parser/arg_parser.sh
:
#!/bin/bash
parse_args() {
local positional_args="$1"
# Specify default argument values.
local apply_certs_flag='false'
local check_http_flag='false'
local check_https_flag='false'
local generate_certs_flag='false'
local project_name_flag='false'
local port_flag='false'
while [[ $# -gt 0 ]]; do
echo "dollar1=$1"
echo "positional_args=$positional_args"
echo "dollar hashtag=$#"
case $1 in
-a|--apply-certs)
apply_certs_flag='true'
shift # past argument
;;
-ch|--check-http)
check_http_flag='true'
shift # past argument
;;
-cs|--check-https)
check_https_flag='true'
shift # past argument
;;
-g|--generate-certs)
generate_certs_flag='true'
shift # past argument
;;
-n|--project-name)
project_name_flag='true'
project_name="$2"
assert_is_non_empty_string "${project_name}"
shift # past argument
shift
;;
-p|--port)
port_flag='true'
port="$2"
assert_is_non_empty_string "${project_name}"
shift # past argument
shift
;;
-*)
echo "Unknown option $1"
print_usage
exit 1
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
}
Expected output
When I run ./src/main.sh -a
, I would expect the output:
echo "POSITIONAL_ARGS=-a
dollar1=-a
positional_args=-a
dollar_hashtag=1
dollar1=-a
positional_args=-a
dollar_hashtag=0
Output
However, instead of parsing/eating/shifting the arguments, the actual output is an infinite loop:
dollar1=
positional_args=
dollar hashtag=1
dollar1=
positional_args=
dollar hashtag=1
dollar1=
positional_args=
dollar hashtag=1
.... etc.
Question
How could I pass the arguments from the CLI from src/main.sh
into the parse_args()
function in the file src/arg_parser/arg_parser.sh
such that they can be parsed?
Notes
I used to have
-p|--prereq)
prerequistes_only_flag='true'
shift # past argument
;;
-*|--*)
echo "Unknown option $1"
print_usage
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
however, shellcheck says the --*)
will never be reached because of the -*|
.
Hypothesis
I think at least one error may be that I, in the original question, treated POSITIONAL_ARGS
as a string instead of an array. However, shellcheck tells me it is an array:
In src/main.sh line 26:
echo "POSITIONAL_ARGS=$POSITIONAL_ARGS"
^--------------^ SC2128 (warning): Expanding an array without an index only gives the first element.