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[@]}"