Reputation: 69
When this script is called
test.sh -a 1 -b 2 -c 3
output is :
-a 1 -b 2
1
I was expecting parseRemainingArgs
to parse and print all arguments.
As you can see all the arguments are passed properly to parseRemainingArgs
as seen in first line of output, but it parses only the first argument.
I set the OPTIND as well but that also didn't help.
Need help in identifying what is wrong with this and how to fix that.
Tried all available answers which google provides but none of them solves this problem.
#!/bin/sh
parseRemainingArgs()
{
echo $@
OPTIND=1
while getopts :ha:b:c: o;
do
case "$o" in
h) echo "Print help";;
a) FIRST_ARG="$OPTARG";;
b) SECOND_ARG="$OPTARG";;
c) THIRD_ARG="$OPTARG";;
\?) ;;
esac
done
echo $FIRST_ARG $SECOND_ARG $THIRD_ARG
}
parseArgs()
{
AllArgs=$@
while getopts :ht: o;
do
case "$o" in
t) TARGET="$OPTARG";;
h) HELP="yes";;
\?) ;;
esac
done
shift $(($OPTIND - 1))
parseRemainingArgs $AllArgs
}
parseArgs $@
Upvotes: 1
Views: 826
Reputation: 753555
To get the problem can you please test your script with the following arguments
gomod.sh -t a -a 1 -b 2 -c 3
In this case
-t
will match inparseArgs
butparseRemainingArgs
will fail to parse the remaining args. It works great when-t
or-h
is not given which matches inparseArgs
.
OK; that's what I'd expect from the script I used (see the script below in the "'Cannot reproduce' response" section — it is a mild extension of what's in the question). From bash
, I get:
$ bash gomod.sh -t a -a 1 -b 2 -c 3
Initial arguments: -t a -a 1 -b 2 -c 3
Doing nothing
OPTIND = 4
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -t a -a 1 -b 2 -c 3
Ignoring error
a1: a2: a3
$
It's a consequence of you setting AllArgs
at the start of parseArgs
. It doesn't change as you parse the arguments in "$@"
in parseArgs
, so you pass the original argument list to parseRemainingArgs
, as shown in the diagnostic output. In parseRemainingArgs
, -t
is not an option, so the loop returns an error on the -t
(hence Ignoring error
), and then exits because a
is not an option.
You can get more output from:
$ bash gomod.sh -t -a 1 -b 2 -c 3
Initial arguments: -t -a 1 -b 2 -c 3
OPTIND = 3
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -t -a 1 -b 2 -c 3
Ignoring error
a1: 1 a2: 2 a3 3
$
It ignores the -t
error in parseRemainingArgs
and then processes each of -a
, -b
and -c
with the corresponding argument.
Alternatively, you could modify the script, replacing the invocation of parseRemainingArgs
with:
parseRemainingArgs "$@"
You can then run it like this, for example, and get the output shown:
$ bash gomod.sh -t antelope -- -a 1 -b 22 -c 333
Initial arguments: -t antelope -- -a 1 -b 22 -c 333
OPTIND = 4
Residual arguments: -a 1 -b 22 -c 333
Parsing Remaining: -a 1 -b 22 -c 333
a1: 1 a2: 22 a3 333
$
Note that replacing the --
with nothing, or with abelone
(or any other non-option) does not work as you'd want.
You could still add a lot more debugging to make it easier to understand, or run it under bash -x
.
I'm not sure what your problem is any more. I've run a slightly modified version of your script — more diagnostics and using back-quotes and expr
to calculate the shift (because some of the shells I tested with don't support the $((…))
arithmetic notation). The code was:
#!/bin/sh
parseRemainingArgs()
{
echo "Parsing Remaining:" "$@"
OPTIND=1
while getopts :ha:b:c: o
do
case "$o" in
h) echo "Print help";;
a) FIRST_ARG="$OPTARG";;
b) SECOND_ARG="$OPTARG";;
c) THIRD_ARG="$OPTARG";;
\?) echo "Ignoring error";;
esac
done
echo a1: $FIRST_ARG a2: $SECOND_ARG a3 $THIRD_ARG
}
parseArgs()
{
AllArgs=$@
echo "Initial arguments:" "$@"
while getopts :ht: o
do
case "$o" in
t) TARGET="$OPTARG";;
h) HELP="yes";;
\?) echo "Doing nothing";;
esac
done
#shift $(($OPTIND - 1))
echo "OPTIND = $OPTIND"
shift `expr $OPTIND - 1`
echo "Residual arguments:" "$@"
parseRemainingArgs $AllArgs
}
parseArgs "$@"
The shells tested were:
sh
— a link to bash
bash
dash
ksh
— Korn shellzsh
hsh
— Heirloom shell (close to Bourne shell)svr4-sh
— Unix System V Release 4 (slightly souped up Bourne shell)Of these, hsh
and svr4-sh
did not accept shift $(($OPTIND - 1))
. I'm slightly surprised the Heirloom shell recognizes getopts
at all.
The script was called gomod.sh
, and the output I got was:
$ for shell in sh bash dash ksh zsh hsh svr4-sh
> do
> boxecho $shell
> $shell gomod.sh -a 1 -b 2 -c 3
> echo
> done
********
** sh **
********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
**********
** bash **
**********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
**********
** dash **
**********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*********
** ksh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*********
** zsh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 -b 2 -c 3 a2: a3
*********
** hsh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*************
** svr4-sh **
*************
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
$
boxecho
is a trivial script that echoes its arguments inside a box of stars:
echo "** $@ **" | sed -e h -e 's/./*/g' -e p -e x -e p -e x
I hadn't modified it since 1998; I don't often have a subdirectory s
with a subdirectory inside that contains a file g
, so the absence of quotes around the s/./*/g
hadn't ever bit me, but I've fixed the script above (and in my bin
directory). There are other ways of writing the sed
too; this does, however, work.
It is worth noting that the use of getopts
in the parseRemainingArgs
function works with the full set of arguments originally passed to the command, despite the implication that it is working on 'the remaining arguments'. You'd pass "$@"
to the function if it were to process the as yet unprocessed arguments. It would also run into 'end of arguments' immediately unless you have a GNU getopts
functionality without POSIXLY_CORRECT set in the environment since the first such argument would be 1
which is not an option and would terminate the getopts
processing.
Upvotes: 2