Reputation: 1116
I would like to create a bash script which can be executed as follows:
bash myscript.sh -n n -k k -p1 p1 -p2 p2
where n,k,p1,p2
are some parameters and -n, -k, -p1, -p2
indicate which parameters are specified. I would like to be able to parse them. The obvious solution would be to use getopt
, which doesn't work since there are options with name consisting of more than one character. Iterating tthrought the $@
variable like this:
for op in $@ ...
is no good either, since I cannot access a neighbor. Is there a way to iterate through the list 'normally', something like this?
for i in {1..$@}; do
case ${$i} in
-n) # do something, accessing ${i+1}
...
esac
done
Upvotes: 1
Views: 169
Reputation: 437111
John Kugelman's helpful answer is the way to go, but just to show how it could be done with array-index-based access to $@
, the (pseudo) array of input arguments, involving shell arithmetic:
for (( i = 1; i <= $#; i+=2 )); do
this=${!i} # get argument with (1-based) index i; same as: ${@:i:1}
next=${@:i+1:1} # get argument i + 1
case $this in
-n) echo "$this value: [$next]";;
-k) echo "$this value: [$next]";;
-p1) echo "$this value: [$next]";;
-p2) echo "$this value: [$next]";;
*) echo "Unrecognized option: $this" >&2; exit 1;;
esac
done
Note that this snippet makes the following assumptions, which makes it unsuitable for most real-world uses:
As for what you've tried:
By {1..$@}
you probably meant {1..$#}
in order to enumerate all indices ($#
is the count of input arguments), which, however, doesn't work either, because brace expansion only works with literal values, unfortunately. There are workarounds, but they're not pretty.
Note that the usual way of obtaining the list of indices from a Bash array does not work with pseudo-array $@
:
${!@}
yields the empty string
whereas copying $@
to a true array first does work: args=( "$@" ); echo "${!args[@]}"
The best solution in this case is to use the C-style for
loop demonstrated above.
${$i}
in an attempt to access the i
-th argument is syntactically invalid in Bash; what does work, however, is to use variable indirection:Thanks, rici
With $i
equaling 2
, ${!i}
is equivalent to $2
, for instance.
This syntax, however, doesn't work with calculated indices (unless you awkwardly define additional variables such as iPlus1=$(( i + 1 ))
and then use ${!iPlus1}
).
Fortunately, however, when it comes to index-based element access, $@
does behave like regular Bash arrays (except that the first element has index 1
, not 0
):
Thus, ${!i}
is equivalent to ${@:i:1}
, which returns the (1
) element with index $i
- note how I've used no $
prefix when referring to $i
in the index spec., because it is evaluated in an arithmetic context, where $
-prefixing variable references is optional and simple integer calculations can be performed.
This enables element access based on calculating the index: the element with index $i + 1
can be accessed with ${@:i+1:1}
.
Upvotes: 2
Reputation: 361565
You can use shift
to remove options after you've processed them.
while (($# > 0)); do
case $1 in
-n) use $2; shift 2;;
-k) use $2; shift 2;;
-p1) use $2; shift 2;;
-p2) use $2; shift 2;;
*) echo "Unrecognized option $1" >&2; exit 1;;
esac
done
Upvotes: 3