Let me start by saying, I hate bash. I should stop using it for anything remotely more complicated than running a bunch of commands in series. I don’t know if this already exists in bash, but I couldn’t find it and I needed a data structure in a hurry, so here it is.
Since you can’t really pass pointers or references to arrays and you can’t return anything other than integers, here’s how to implement push, pop, shift, and unshift in a bash shell script.
arr_push() {
arr=("${arr[@]}" "$1")
}
arr_pop() {
i=$(expr ${#arr[@]} - 1)
placeholder=${arr[$i]}
unset arr[$i]
arr=("${arr[@]}")
}
arr_shift() {
arr=("$1" "${arr[@]}")
}
arr_unshift() {
placeholder=${arr[0]}
unset arr[0]
arr=("${arr[@]}")
}
Now for some wicked simple examples…
arr=("one" "two" "three")
echo ${arr[@]}
one two three
arr_shift "zero"
arr_push "four"
echo ${arr[@]}
zero one two three four
arr_unshift
echo ${arr[@]}
one two three four
arr_pop
echo ${arr[@]}
one two three
eu fiz em um método muito diferente e mais fácil …
array+=(hmm)
they can be, but I didn’t really care what they looked like or how they performed. they got the job done and were throwaway functions anyway. It wasn’t worth the time to pretty them up. I wrote the first thing that came to mind when I approached the situation and that was what popped out.
Pop/push/shift/unshift in Bash are much less horrendous then you describe – see after the break.
BTW – you have shift and unshift confused in your code: shift discards the first element by shifting the all elements to the left so that element 1 is now element 0, while unshift is “push from the other side” – it sticks an element at the beginning of the array shifting all the other elements to the right.
True – the syntax may not be pretty (for some operations), especially if you are used to object oriented languages like ECMAScript (ne, Javascript), but its usable and understandable:
Push:
arr+=(newvalue)
[Kudo matheus]
Pop:
unset arr[${#arr[*]}-1]
[Kudo Jo Liss]
This just truncates the array by removing the last element. If you want to actually get it first, then just assign ${arr[${#arr[*]}-1]} to something before hand.
Shift:
arr=(“${arr[@]:1}”)
This uses the “slice syntax” which you can use to get any view of the array. Again, you don’t get to keep the shifted argument so if you want it, assign it elsewhere first.
Unshift:
arr=(newvalue “${arr[@]}”)
This is extremely inefficient:
$ TIMEFORMAT=%R; time arr=({1..1000000}); time arr=(“${arr[@]}” 1); time arr+=(1)
1.914
3.765
0.000
Also, let me point out to Jo Liss that arr=(“${arr[@]}”) isn’t no-op; bash arrays can be sparse, so reassigning removes the wholes.
Just use the recommended syntax to append ( foo+=(“bar”) which is extremely cheap ); if you require shifting or popping, then the thing gets pretty difficult… the fact that bash arrays are sparse makes that more difficult (there’s no guarantee that ${#arr[@]}-1 is the last element and you can’t mix the ${!arr[@]} expansion with the ${arr[@]: -1} selector…)
Ups, s/wholes/holes/
This is a more generic version
function push_element {
[ $# -gt 1 ] || return 0
eval $1=\(\”\${$1[@]}\” \”\${@:2:99}\”\)
}
function pop_element {
local ArgCnt
local ArrayVal
eval ArrayVal=\(\”\${$1[@]}\”\)
ArgCnt=${#ArrayVal[@]}
(( ArgCnt — ))
echo “${ArrayVal[$ArgCnt]}”
eval $1=\(“\${$1[@]:0:$ArgCnt}”\)
}
function shift_element {
local ArrayVal
eval ArrayVal=\(\”\${$1[@]}\”\)
echo “${ArrayVal[0]}”
eval $1=\(“\${$1[@]:1:99}”\)
}
function unshift_element {
[ $# -gt 1 ] || return 0
eval $1=\(\”\${@:2:99}\” \”\${$1[@]}\” \)
}
Test Code
TestArray=(testq testg sdgsdg)
echo TestArray=”${TestArray[@]}”
push_element TestArray Push
echo TestArray=”${TestArray[@]}”
unshift_element TestArray Shift
echo TestArray=”${TestArray[@]}”
pop_element TestArray
echo TestArray=”${TestArray[@]}”
shift_element TestArray
echo TestArray=”${TestArray[@]}”
2:25 pm
(Came here looking for a sane way to do array handling in Bash. Apparently it doesn’t exist.)
Out of curiosity, why are you doing arr=(“${arr[@]}”)? Isn’t that a no-op?
Some of these functions can be simplified, by the way. For example,
unset arr[${#arr[@]}-1]
is how I do arr_pop in a single expression. If you squint, it’s almost readable.