Danny
Danny

Reputation: 209

Unshift array element in Bash script

I would like to add elements to the beginning of an array instead of to the end. Is this possible in Bash?

Upvotes: 8

Views: 5190

Answers (4)

milahu
milahu

Reputation: 3579

shift and unshift are useful to loop over a dynamic stack array
so the array can be modified inside the loop

for example, this is needed for argparse in bash (one, two, three)
to parse concatenated short options, or to read arguments from file

example with shift and unshift

#!/usr/bin/env bash

# bash-stack-while-loop.sh

# stack allows to unshift args
stack=("$@")
stack=(-a -b -ab -c cval uval) # example
while [ ${#stack[@]} != 0 ]; do
  arg="${stack[0]}"; stack=("${stack[@]:1}") # shift arg
  case "$arg" in
    -a) echo "arg a"; continue;;
    -b) echo "arg b"; continue;;
    -c) val="${stack[0]}"; stack=("${stack[@]:1}");
      echo "arg c: ${val@Q}"; continue
    ;;
    -[^-]*)
      # unshift args: expand concatenated short options
      # example: -vvv -> -v -v -v
      pre_stack=()
      for ((i=1;i<${#arg};i++)); do
        #arg2="${arg:$i:1}"; echo "unshifting ${arg2@Q}"
        pre_stack+=("-${arg:$i:1}")
      done
      stack=("${pre_stack[@]}" "${stack[@]}") # prepend args to stack
      continue
    ;;
    *) echo "unknown arg: ${arg@Q}";;
  esac
done

example output

arg a
arg b
arg a
arg b
arg c: 'cval'
unknown arg: 'uval'

this answer was moved from how to shift array value in bash

Upvotes: 0

agc
agc

Reputation: 8406

Non-bash version: POSIX shells don't really have arrays, excepting shell parameters, (i.e. $1, $2, $3, ...),but for those parameters this should work:

set - a b c ; echo $1 $3

Output:

a c

Now add "foo" to the beginning:

set - foo "$@" ; echo $1 $3

Output:

foo b

Upvotes: 6

Siguza
Siguza

Reputation: 23870

If your array is contiguous, you can use the "${array[@]}" syntax to construct a new array:

array=('a' 'b' 'c');
echo "${array[@]}"; # prints: a b c
array=('d' "${array[@]}");
echo "${array[@]}"; # prints: d a b c

As chepner mentions, the above method will collapse indices of sparse arrays:

array=([5]='b' [10]='c');
declare -p array; # prints: declare -a array='([5]="b" [10]="c")'
array=('a' "${array[@]}");
declare -p array; # prints: declare -a array='([0]="a" [1]="b" [2]="c")'

(Fun fact: PHP does that too - but then again, it's PHP :P)

If you need to work with sparse arrays, you can iterate over the indices of the array manually (${!array[@]}) and increase them by one (with $((...+1))):

old=([5]='b' [10]='c');
new=('a');
for i in "${!old[@]}"; do
    new["$(($i+1))"]="${old[$i]}";
done;
declare -p new; # prints: declare -a new='([0]="a" [6]="b" [11]="c")'

Upvotes: 12

AtomHeartFather
AtomHeartFather

Reputation: 958

Yes, it is possible, see the example below:

#!/bin/bash
MyArray=(Elem1 Elem2);
echo "${MyArray[@]}"
MyArray=(Elem0 "${MyArray[@]}")
echo "${MyArray[@]}"

As per @ghoti's comment, declare -p MyArray can be used to display the content of the array nicely. When invoked at the end of the script above, it outputs:

declare -a MyArray='([0]="Elem0" [1]="Elem1" [2]="Elem2")'

Upvotes: 4

Related Questions