I would like to iterate over the arguments given to a bash script, for example:
./bash_script file1 file2 file3
$@
gives me all the files given to the script but how do I iterate over the files?
I would like to cat
each file and remove the contents using awk
(I know how to do that, it's the unpacking of $@
that's got me confused).
The trick is to double quote it as in "$@"
.
foo(){
printf ' ->%s\n' "$@";
}
foo "a b" c "d e"
is equivalent to:
printf ' ->%s\n' "a b" c "d e"
If the $@
in the above is not double-quoted, then you'll get:
printf ' ->%s\n' a b c d e
due to word splitting on $IFS
characters ( $IFS
defaults to ' '$'\t'$'\n'
, i.e. a space, a tab, and a newline )
$@ vs. $*
Double quotes work like this for any @-expansion on any array:
$ foo=( "a b" c "d e" )
$ printf ' ->%s\n' "${foo[@]}"
->a b
->c
->d e
In contrast, *-expansions (e.g., $*
, ${foo[*]}
) will use the first character of $IFS
to join the items of an array into a single string:
$ foo=( "a b" c "d e" )
$ ( IFS=:; printf ' ->%s\n' "${foo[*]}" )
->a b:c:d e
which, if left unquoted, will again split on this very IFS character:
$ foo=( "a b" c "d e:f:g" )
$ ( IFS=:; printf ' ->%s\n' ${foo[*]} )
->a b
->c
->d e
->f
->g
Trick for iterating over $@ in for-loops:
The "$@"
array is special. If you want to iterate over "$@"
in a for loop, then you can abbreviate
for variable_name in "$@"; do
...
done
as
for variable_name; do
done
as skipping the in something
part of a for
loop implies in "$@"
.
This works even in POSIX-only shells too (dash, bourne shell) which don't have array variables but support "$@"
and "$*
.