You can use the argument list $@
as an array in POSIX shells
It's trivial to initialize, shift
, unshift
, and push
:
# initialize $@ containing a string, a variable's value, and a glob's matches
set -- "item 1" "$variable" *.wav
# shift (remove first item, accepts a numeric argument to remove more)
shift
# unshift (prepend new first item)
set -- "new item" "$@"
# push (append new last item)
set -- "$@" "new item"
Here's a pop
implementation:
# pop (remove last item, store it in $last)
i=0 len=$#
for last in "$@"; do
if [ $((i+=1)) = 1 ]; then set --; fi # increment $i. first run: empty $@
if [ $i = $len ]; then break; fi # stop before processing the last item
set -- "$@" "$last" # add $a back to $@
done
echo "$last has been removed from ($*)"
($*
joins the contents of $@
with $IFS
, which defaults to a space character.)
Iterate through the $@
array and modify some of its contents:
i=0
for a in "$@"; do
if [ $((i+=1)) = 1 ]; then set --; fi # increment $i. first run: empty $@
a="${a%.*}.mp3" # example tweak to $a: change extension to .mp3
set -- "$@" "$a" # add $a back to $@
done
Refer to items in the $@
array:
echo "$1 is the first item"
echo "$# is the length of the array"
echo "all items in the array (properly quoted): $@"
echo "all items in the array (in a string): $*"
[ "$n" -ge 0 ] && eval "echo \"the ${n}th item in the array is \$$n\""
(eval
is dangerous, so I've ensured $n
is a number before running it)
There are a few ways to set $last
to the final item of a list without popping it:
with a function:
last_item() { shift $(($# - 1)) 2>/dev/null && printf %s "$1"; }
last="$(last_item "$@")"
... or with an eval
(safe since $#
is always a number):
eval last="\$$#"
... or with a loop:
for last in "$@"; do true; done
⚠️ Warning: Functions have their own $@
arrays. You'll have to pass it to the function, like my_function "$@"
if read-only or else set -- $(my_function "$@")
if you want to manipulate $@
and don't expect spaces in item values.
If you need to handle spaces in item values, it becomes much more cumbersome:
# ensure my_function() returns each list item on its own line
i=1
my_function "$@" |while IFS= read line; do
if [ $i = 1 ]; then unset i; set --; fi
set -- "$@" "$line"
done
This still won't work with newlines in your items. You'd have to escape them to another character (but not null) and then escape them back later. See "Iterate through the $@
array and modify some of its contents" above. You can either iterate through the array in a for
loop and then run the function, then modify the variables in a while IFS= read line
loop, or just do it all in a for
loop without a function.
flat wrong
(probably done by M$). – Torpedomanresizable arrays
. But even if it was not about array, it should execute correctly according to the reference sheet. But you are true, the important part is that there is no concept of arrays. – Soritesfunction
keyword, magic$RANDOM
andecho -n
, recommendstrap exit ERR
rather than a more usefultrap 'exit 1' ERR
, and is extremely reckless with quoting. Not recommended. – Hel