问题
If I want to iterate over all arguments it is as easy as for i in "$@"; do ...
. However, let's say I want to start with the second argument and also make use of the arguments' positions for some basic calculation.
As an example I want to shorten these commands into one loop:
grep -v 'foobar' "$2" | grep -f $file > output1.txt
grep -v 'foobar' "$3" | grep -f $file > output2.txt
grep -v 'foobar' "$4" | grep -f $file > output3.txt
grep -v 'foobar' "$5" | grep -f $file > output4.txt
I tried many variations like for i in {2..5}; do grep -v 'foobar' "$$i" | grep -f $file > output$(($i-1)).txt; done
; however, it seems bash expansion doesn't work like this.
EDIT:
Seems I made a mistake not emphasizing that I need to make use of the argument's position/number (i.e., 2 from $2
). It's important because the output files get used separately later in the script. All of the provided answers so far seem correct but I don't know how to use them to make use of the argument's "number".
回答1:
If you do not want to shift off the first unneeded arguments you can use the indirection expansion:
for i in {2..5}; do
echo "${!i}"
done
回答2:
Couple correct answers already, another way could be:
for (( i=2; i <= "$#"; i++ )); do
echo "arg position: ${i}"
echo "arg value: ${!i}"
done
回答3:
You can also make use of array indexes directly:
#!/bin/bash
for i in "${@:2}"; do
echo "$i"
done
Which will iterate over the arguments beginning with the second argument. It will also preserve whitespace in arguments when quoted. e.g.
$ bash args.sh one two "three four" five
two
three four
five
回答4:
You just need to use shift
to move positional parameter once (thus discarding $1
):
fn() { arg1="$1"; shift; for arg; do echo "$arg"; done; }
Call it as:
fn val1 val2 val3 val4
Output:
val2
val3
val4
回答5:
Above answers are correct. Another approach:
a=("${@:2}")
for i in ${!a[@]}; do
echo "$i = ${a[i]}"
done
回答6:
$@
is similar to an array, with the small difference that it is one-based and not zero-based ($0
is the name of the shell or shell script), and it can't be indexed (${@[1]}
won't work to access $1
). You can use parameter expansion (more specifically: substring expansion) to access a subset of your positional parameters:
$ set arg1 arg2 arg3 arg4 arg5 # Set $1, $2, $3, $4, $5
$ for i in "${@:2}"; do echo "$i"; done
arg2
arg3
arg4
arg5
Notice that this will not actually allow you to use the index of the argument and the argument itself. To do that, you'd have to either turn the positional parameters into a regular array and deal with the index offset by one ($1
would have index 0), or use indirect expansion (see andlrc's answer).
Example of the first method:
$ set arg1 arg2 arg3 arg4 arg5
$ args=("$@")
$ for i in 3 2 5 1 4; do echo "\$$i is ${args[$((i-1))]}"; done
$3 is arg3
$2 is arg2
$5 is arg5
$1 is arg1
$4 is arg4
Clearly, ${args[$((i-1))]}
is rather messy, but it works.
Side note: if you want to access all positional parameters, you can use a convenient shorthand:
$ for i; do echo "$i"; done
arg1
arg2
arg3
arg4
arg5
for
without the in
part loops over all positional parameters (see manual).
来源:https://stackoverflow.com/questions/37053780/iterate-over-arguments-in-a-bash-script-and-make-use-of-their-numbers