bash array list - ghdrako/doc_snipets GitHub Wiki

Array: An array is a numbered list of strings: It maps integers to strings.

$ files=(~/*.jpg); cp "${files[@]}" /backups/

Array

Declare

# Declare a list
# declare -a mylist # Can do this, or `local -a` or `readonly -a` or:
mylist[0]='foo' # This both declares and assigns to mylist[0]
# OR Both declares & assigns:
#mylist=(foo bar baz three four "five by five" six)

Inicialize

# Push or assign, note the += and ()
###mylist=(bar) # Would overwrite mylist[0]
mylist+=(bar) # mylist[1]
mylist+=(baz) # mylist[2]
mylist+=(three four) # mylist[3] AND mylist[4]
mylist+=("five by five") # mylist[5] Note spaces and quotes
mylist+=("six") # mylist[6]
# OR APPEND, note the "+" and we're assuming foo was already assigned
#mylist+=(bar baz three four "five by five" six

Access element

Accessing Elements:
- Syntax: ${array_name[index]}
# initialize the names array
names[0]="john"
names[1]="nancy"
names[2]="jane"
names[3]="steve"
names[4]="bob"
# display the first and second entries
echo "First Index: ${names[0]}"
echo "Second Index: ${names[1]}"

Access all the items in an array

${array_name[*]}
${array_name[@]}

Initialize array and print values

$ cat names.txt
Jane Smith
John Jones
Dave Edwards

$ cat  array-from-file.sh
#!/bin/bash
numbers="1 2 3 4 5 6 7 8 9 10"
array1=( `echo "$numbers"` )     # inicialize array
total1=0
total2=0
for num in "${array1[@]}"
do
 #echo "array item: $num"
total1+=$num
let total2+=$num
done
echo "Total1: $total1"
echo "Total2: $total2"

Iterate over the values

echo -e "\nforeach \"\${!mylist[@]}\":"
for element in "${!mylist[@]}"; do
  echo -e "\tElement: $element; value: ${mylist[$element]}"
done

Slice array - subset

echo -e "\nStart from element 5 and show a slice of 2 elements:"
printf "%q|" "${mylist[@]:5:2}"
letters=( "A""B""C""D""E" )
b=${letters:0:2}
echo "${b}"
AB

letters=( "A""B""C""D""E" )
b=${letters::5} # do 5 bez niego
echo "${b}"
ABCDE

letters=( "A""B""C""D""E" )
b=${letters:3} # wszystkie od 3 wlacznie czyli od D
echo "${b}"
DE

Shift first element

echo -e "\nShift FIRST element [0] (dumped before and after):"
declare -p mylist | $FORMAT # Display before
mylist=("${mylist[@]:1}") # First element, needs quotes
#mylist=("${mylist[@]:$count}") # First #count elements
declare -p mylist | $FORMAT # Display after

Pop last element

echo -e "\nPop LAST element (dumped before and after):"
declare -p mylist | $FORMAT
unset -v 'mylist[-1]' # bash v4.3+
#unset -v "mylist[${#mylist[*]}-1]" # Older
declare -p mylist

Append and Delete

colors=("Red" "Green" "Blue")
colors+=("Yellow") Append an element
unset colors[1] Delete an element

Delete slice

echo -e "\nDelete element 2 using unset (dumped before and after):"
declare -p mylist
unset -v 'mylist[2]'
declare -p mylist

Delete the entire list

unset -v 'mylist'

Inicialize from file

#!/bin/bash
names="names.txt"
contents1=( `cat "$names"` )
echo "First loop:"
for w in "${contents1[@]}"
do
  echo "$w"
done
IFS=""
names="names.txt"
contents1=( `cat "$names"` )
echo "Second loop:"
for w in "${contents1[@]}"
do
  echo "$w"
done

Output:
First loop:
Jane
Smith
John
Jones
Dave
Edwards
Second loop:
Jane Smith
John Jones
Dave Edwards


The second loop is the same code as the first loop, but this time with the value of IFS equal to "', which has the effect of using the newline as a separator, one data element per row.

Update

array=("I" "love" "deep" "dish" "pizza")
#the first array element:
echo ${array[0]}
#all array elements:
echo ${array[@]}
#all array indexes:
echo ${!array[@]}
#Remove array element at index 3:
unset array[3]
#add new array element with index 1234:
array[1234]="in Chicago"
#all array elements:
echo ${array[@]}

Access element

#!/bin/bash
# method #1:
fruits[0]="apple"
fruits[1]="banana"
fruits[2]="cherry"
fruits[3]="orange"
fruits[4]="pear"
echo "first fruit: ${fruits[0]}"
# method #2:
declare -a fruits2=(apple banana cherry orange pear)
echo "first fruit: ${fruits2[0]}"
# range of elements:
echo "last two: ${fruits[@]:3:2}"
# substring of element:
echo "substring: ${fruits[1]:0:3}"
arrlength=${#fruits[@]}
echo "length: ${#fruits[@]}"

Array loop

$ cat array-loops.sh
#!/bin/bash
fruits[0]="apple"
fruits[1]="banana"
fruits[2]="cherry"
fruits[3]="orange"
fruits[4]="pear"
# array length:
arrlength=${#fruits[@]}
echo "length: ${#fruits[@]}"
# print each element via a loop:
for (( i=1; i<${arrlength}+1; i++ ));
do
  echo "element $i of ${arrlength} : " ${fruits[$i-1]}
done

Output:
length: 5
element 1 of 5 : apple
element 2 of 5 : banana
element 3 of 5 : cherry
element 4 of 5 : orange
element 5 of 5 : pear
$ cat array-function.sh
#!/bin/bash
items() {
for line in "${@}"
do
printf "%s\n" "${line}"
done
}
arr=( 123 -abc 'my data' )
items "${arr[@]}"

Output:
123
-abc
my data


breeds=(husky setter "border collie" chiwawa)
breeds[0]
breeds[1]
${#breeds[@]} #total number of items

array0=(one two three four five six) # Declare an array with 6 elements
echo $array0 # => "one"              # Print first element
echo ${array0[0]} # => "one"         # Print first element
echo ${array0[@]} # => "one two three four five six" # Print all elements
echo ${#array0[@]} # => "6"          # Print number of elements
echo ${#array0[2]} # => "5"          # Print number of characters in third element
echo ${array0[@]:3:2} # => "four five" # Print 2 elements starting from forth
for i in "${array0[@]}"; do            # Print all elements. Each of them on new line.
    echo "$i"
done

Declare

declare -a arr
local -a arr
readonly -a arr
arr=(Hello World)
mylist=() 
mylist[0]=foo
mylist\+=(bar) # push to list element
names=("Bob" "Peter" "$USER" "Big Bad John")

# Specify explicit indexes
names=([0]="Bob" [1]="Peter" [20]="$USER" [21]="Big Bad John")
names[0]="Bob"

# fill an array with filenames using Globs
photos=(~/"My Photos"/*.jpg)
$ files=$(ls)    # BAD, BAD, BAD!
$ files=($(ls))  # STILL BAD!
$ files=(*)      # Good!

names=('Alex' 'Ada' 'Alexandra')
names+=('Soto') # Appends element
unset names[3] # Removes element
echo ${names[0]} # Alex
echo ${names[@]} # Alex Ada Alexandra
echo ${#names[@]} # 3 
  arr[0]=Hello
  arr[1]=World

Delete

unset -v array # Erase an array
unset -v array[@] # Completely erases the array, array .
unset -v array[*]
unset -v array[i]  # Erases the i th array value from array .

Refer to array item

 echo ${arr[0]} ${arr[1]}  # The braces are required to avoid conflicts with pathname expansion
  ${arr[*]}         # All of the items in the array
  ${!arr[*]}        # All of the indexes in the array
  ${#arr[*]}        # Number of items in the array
  ${#arr[0]}        # Length of item zero

Print all elements from a plain array:

The declare -p command prints the contents of one or more variables.

$ declare -p myfiles
declare -a myfiles='([0]="/home/wooledg/.bashrc" [1]="billing codes.xlsx" [2]="hello.c")'

print the array using printf

$ printf '%s\n' "${myfiles[@]}"
/home/wooledg/.bashrc
billing codes.xlsx
hello.c
$ for file in "${myfiles[@]}"; do
>     cp "$file" /backups/
> done`
for i in "${names[@]}"; do
  echo "Hello $i"
done

Print keys and values of all elements from a key/value array:

for key in "${!score[@]}"; do
  echo $key
done
for val in "${score[@]}"; do
  echo $val
done

Good Practice: Always quote your array expansions properly, just like you would your normal parameter expansions. Use "${myarray[@]}" to expand all your array elements and ONLY use "${myarray[*]}" when you want to merge all your array elements into a single string.

Expanding Inddexes

You may need to refer to multiple elements at the same time, or refer to the same index in multiple arrays at the same time. In these cases, it's better to expand the array indexes, instead of the array values.

$ first=(Jessica Sue Peter)
$ last=(Jones Storm Parker)
$ for i in "${!first[@]}"; do
> echo "${first[i]} ${last[i]}"
> done
Jessica Jones
Sue Storm
Peter Parker

"${!arrayname[@]}" expands to a list of the indices of an array, in sequential order.

Let's suppose we want to process an array two elements at a time, and let's also suppose we know this array can't be sparse. Then:

$ a=(a b c q w x y z)
$ for ((i=0; i<${#a[@]}; i+=2)); do
> echo "${a[i]} and ${a[i+1]}"
> done

Sparse Arrays

$ nums=(zero one two three four)
$ nums[70]="seventy"
$ unset 'nums[3]'
$ declare -p nums
declare -a nums='([0]="zero" [1]="one" [2]="two" [4]="four" [70]="seventy")'

This can be useful if you want to re-index an array to remove all of the gaps:

$ array=("${array[@]}")      # This re-creates the indexes with no gap.

Output

declare -a mylist=([0]="foo" [1]="bar" [2]="baz" [3]="one" [4]="two" [5]="three four")
${mylist[@]}   = foo|bar|baz|one|two|three|four|
${mylist[*]}   = foo|bar|baz|one|two|three|four|
"${mylist[@]}" = foo|bar|baz|one|two|three\ four|
"${mylist[*]}" = foo\ bar\ baz\ one\ two\ three\ four|        # But this is broken!

foreach "${!mylist[@]}":
       Element: 0; value: foo
       Element: 1; value: bar
       Element: 2; value: baz
       Element: 3; value: one
       Element: 4; value: two
       Element: 5; value: three four

But don't do this: ${mylist[*]}
       Element: foo; value: foo
       Element: bar; value: foo
       Element: baz; value: foo
       Element: one; value: foo
       Element: two; value: foo
       Element: three; value: foo
       Element: four; value: foo

Start from element 3 and show a slice of 2 elements:
one|two|

Shift FIRST element [0] (dumped before and after):
declare -a mylist=([0]="foo" [1]="bar" [2]="baz" [3]="one" [4]="two" [5]="three four")
declare -a mylist=([0]="bar" [1]="baz" [2]="one" [3]="two" [4]="three four")

Pop LAST element (dumped before and after):
declare -a mylist=([0]="bar" [1]="baz" [2]="one" [3]="two" [4]="three four")
declare -a mylist=([0]="bar" [1]="baz" [2]="one" [3]="two")

Delete element 2 using unset (dumped before and after):
declare -a mylist=([0]="bar" [1]="baz" [2]="one" [3]="two")
declare -a mylist=([0]="bar" [1]="baz" [3]="two

Join elements from array

Two version of function

# "Join" the values
function Join { local IFS="$1"; shift; echo "$*"; } # One character delimiter!
# Note that the Join above requires "$*" and not "$@"!
echo -en "\nJoin ',' \${mylist[@]} = "; Join ',' "${mylist[@]}"
function String_Join {
local delimiter="$1"
local first_element="$2"
shift 2
printf '%s' "$first_element" "${@/#/$delimiter}"
# Print first element, then reuse the '%s' format to display the rest of
# the elements (from the function args $@), but add a prefix of $delimiter
# by "replacing" the leading empty pattern (/#) with $delimiter.
}
echo -n "String_Join '<>' \${mylist[@]} = " ; String_Join '<>' "${mylist[@]}"