When I do something like ARGS="$@"
, the shell just concatenates the arguments stored in "$@"
. Is there a way to store "$@"
into a variable in pure POSIX shell to be able to use it to call another program or function with the passed arguments?
The only array-like structure in a POSIX shell is the $@
list whose elements can be accessed independently with $1
,$2
,$3
,... The number of elements available is given by $#
.
$@
elements can be modified using set
and shift
.
The top-level and each function call has its own separate $@
array which is initialised to the arguments received by the script/function when it is called.
Using set
or shift
inside a function does not affect the top-level $@
.
It is possible to save all the original elements of $@
somewhere but I don't believe there is way to use the result in a form as simple as bash's "${arr[@]}"
syntax. (Individual elements may accessed without too much effort but not, trivially, the array as a whole.)
However, by appropriately reloading/manipulating the elements of $@
it can be used directly, although performing the manipulation is likely to be rather tedious.
A quick search for ways to accomplish the saving found these approaches:
declare_i_arr
)array_init
)Rich's code from the second link is probably the simplest and looks like:
save(){
for i do
printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
done
echo " "
}
and is used as:
myarray=$(save "$@")
set -- foo bar baz boo
# ... do stuff with new $@ ...
eval "set -- $myarray"
The eval
is safe since $myarray
expands to a list of single-quoted strings.
See
how-to-use-pseudo-arrays-in-posix-shell-script
for a more efficient and arguably easier to understand awk
implementation of this idea. Here's the performance difference between the 2 implementations (timed using bash):
$ cat tst.sh
#!/usr/bin/env bash
save_sed(){
for i do
printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
done
echo " "
}
save_awk() {
LC_ALL=C awk -v q=\' '
BEGIN {
for ( i=1; i<ARGC; i++ ) {
gsub(q, q "\\" q q, ARGV[i])
printf "%s ", q ARGV[i] q
}
print ""
}
' "$@"
}
echo "calling sed in a loop:"
time save_sed >/dev/null $(seq 100)
echo ""
echo "calling awk once:"
time save_awk >/dev/null $(seq 100)
$ ./tst.sh
calling sed in a loop:
real 0m3.403s
user 0m1.014s
sys 0m2.615s
calling awk once:
real 0m0.042s
user 0m0.015s
sys 0m0.031s
The awk versions performance hardly changes when the number of arguments change while the loop+sed version increases/decreases by about a factor of 10 when the number of arguments changes by the same factor of 10.