myshell.sh:
#!/bin/bash
script_args=()
while [ $OPTIND -le "$#" ]
do
if getopts h:d:s: option
then
case $option
in
h) host_name="$OPTARG";;
d) wait_time="$OPTARG";;
s) script="$OPTARG";;
esac
else
script_args+=("${!OPTIND}")
((OPTIND++))
fi
done
"$script" "${script_args[@]}"
test.sh:
#!/bin/bash
echo "$0 $@"
Testing the OP's cases:
$ PATH+=:. # Use the cases as written without prepending ./ to the scripts
$ myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3
./test.sh param1 param2 param3
$ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
./test.sh param1 param2 param3
$ myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3
./test.sh param1 param2 param3
What's going on:
getopts
will fail if it encounters a positional parameter. If it's used as a loop condition, the loop would break prematurely whenever positional parameters appear before options, as they do in two of the test cases.
So instead, this loop breaks only once all parameters have been processed. If getopts
doesn't recognize something, we just assume it's a positional parameter, and stuff it into an array while manually incrementing getopts
's counter.
Possible improvements:
As written, the child script can't accept options (only positional parameters), since getopts
in the wrapper script will eat those and print an error message, while treating any argument like a positional parameter:
$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./myshell.sh: illegal option -- a
./test.sh param1 param2 opt1 param3
If we know the child script can only accept positional parameters, then myshell.sh
should probably halt on an unrecognized option. That could be as simple as adding a default last case at the end of the case
block:
\?) exit 1;;
$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./myshell.sh: illegal option -- a
If the child script needs to accept options (as long as they don't collide with the options in myshell.sh
), we could switch getopts
to silent error reporting by prepending a colon to the option string:
if getopts :h:d:s: option
Then we'd use the default last case to stuff any unrecognized option into script_args
:
\?) script_args+=("-$OPTARG");;
$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./test.sh param1 param2 -a opt1 param3