#pragma section-numbers 2 <> == How can I use array variables? == This answer assumes you have a basic understanding of what arrays ''are''. If you're new to this kind of programming, you may wish to start with [[BashGuide/Arrays|the guide's explanation]]. This page is more thorough. See [[#See_Also|links]] at the bottom for more resources. <> === Intro === One-dimensional integer-indexed arrays are implemented by Bash, Zsh, and most KornShell varieties including AT&T ksh88 or later, mksh, and pdksh. Arrays are not specified by POSIX and not available in legacy or minimalist shells such as BourneShell and Dash. The POSIX-compatible shells that do feature arrays mostly agree on their basic principles, but there are some significant differences in the details. Advanced users of multiple shells should be sure to research the specifics. Ksh93, Zsh, and Bash 4.0 additionally have [[BashGuide/Arrays#Associative_Arrays|Associative Arrays]] (see also [[BashFAQ/006|FAQ 6]]). This article focuses on indexed arrays as they are the most common type. Basic syntax summary (for bash, math indexed arrays): ||`a=(word1 word2 "$word3" ...)`||Initialize an array from a word list, indexed starting with 0 unless otherwise specified.|| ||`a=(*.png *.jpg)`||Initialize an array with filenames.|| ||`a[i]=word`||Set one element to `word`, evaluating the value of `i` in a math context to determine the index.|| ||`a[i+1]=word`||Set one element, demonstrating that the index is also a math context.|| ||`a[i]+=suffix`||Append `suffix` to the previous value of `a[i]` (bash 3.1).|| ||`a+=(word ...)` # append||<|2> Modify an existing array without unsetting it, indexed starting at one greater than the highest indexed element unless otherwise specified (bash 3.1).|| ||`a+=([3]=word3 word4 [i]+=word_i_suffix)`<
># modify (ormaaj example)|| ||`unset 'a[i]'`||Unset one element. Note the mandatory quotes (`a[i]` is a valid [[glob]]).|| ||`"${a[i]}"`||Reference one element.|| ||`"$(( a[i] + 5 ))"`||Reference one element, in a math context.|| ||`"${a[@]}"`||Expand all elements as a list of words.|| ||`"${!a[@]}"`||Expand all ''indices'' as a list of words (bash 3.0).|| ||`"${a[*]}"`||Expand all elements as a ''single'' word, with the first char of [[IFS]] as separator.|| ||`"${#a[@]}"`||Number of elements (size, length).|| ||`"${a[@]:start:len}"`||Expand a range of elements as a list of words, cf. [[BashFAQ/100#Extracting_parts_of_strings|string range]].|| ||`"${a[@]#trimstart}"` `"${a[@]%trimend}"`<
>`"${a[@]//search/repl}"` etc.||Expand all elements as a list of words, with modifications applied to each element separately.|| ||`declare -p a`||Show/dump the array, in a bash-reusable form.|| ||`mapfile -t a < stream`||Initialize an array from a stream (bash 4.0).|| ||`readarray -t a < stream`||Same as mapfile.|| ||`"$a"`||Same as `"${a[0]}"`. '''Does NOT''' expand to the entire array. This usage is considered '''confusing''' at best, but is usually a '''bug'''.|| Here is a typical usage pattern featuring an array named `host`: {{{#!highlight bash # Bash # Assign the values "mickey", "minnie", and "goofy" to sequential indexes starting with zero. host=(mickey minnie goofy) # Iterate over the indexes of "host". for idx in "${!host[@]}"; do printf 'Host number %d is %s\n' "$idx" "${host[idx]}" done }}} `"${!host[@]}"` expands to the indices of of the `host` array, each as a separate word. Indexed arrays are ''sparse'', and elements may be inserted and deleted out of sequence. {{{#!highlight bash # Bash/ksh # Simple assignment syntax. arr[0]=0 arr[2]=2 arr[1]=1 arr[42]='what was the question?' # Unset the second element of "arr" unset -v 'arr[2]' # Concatenate the values, to a single argument separated by spaces, and echo the result. echo "${arr[*]}" # outputs: "0 1 what was the question?" }}} It is good practice to write your code in such a way that it can handle sparse arrays, even if you think you can guarantee that there will never be any "holes". Only treat arrays as "lists" if you're certain, and the savings in complexity is significant enough for it to be justified. === Loading values into an array === Assigning one element at a time is simple, and portable: {{{#!highlight bash # Bash/ksh arr[0]=0 arr[42]='the answer' }}} It's possible to assign multiple values to an array at once, but the syntax differs across shells. Bash supports only the `arrName=(args...)` syntax. ksh88 supports only the `set -A arrName -- args...` syntax. ksh93, mksh, and zsh support both. There are subtle differences in both methods between all of these shells if you look closely. {{{#!highlight bash # Bash, ksh93, mksh, zsh array=(zero one two three four) }}} {{{#!highlight bash # ksh88/93, mksh, zsh set -A array -- zero one two three four }}} When initializing in this way, the first index will be 0 unless a different index is specified. With compound assignment, the space between the parentheses is evaluated in the same way as the arguments to a command, including [[glob|pathname expansion]] and WordSplitting. Any type of expansion or substitution may be used. All the usual [[Quotes|quoting]] rules apply within. {{{#!highlight bash # Bash/ksh93 oggs=(*.ogg) }}} With ksh88-style assignment using `set`, the arguments are just ordinary arguments to a command. {{{#!highlight bash # Korn set -A oggs -- *.ogg }}} {{{#!highlight bash # Bash (brace expansion requires 3.0 or higher) homeDirs=(~{,root}) # brace expansion occurs in a different order in ksh, so this is bash-only. letters=({a..z}) # Not all shells with sequence-expansion can use letters. }}} {{{#!highlight bash # Korn set -A args -- "$@" }}} ==== Loading lines from a file or stream ==== In bash 4, the `mapfile` command (also known as `readarray`) accomplishes this: {{{#!highlight bash # Bash 4 mapfile -t lines " "${arr[@]}") echo "${tmp%<=>}" # Remove the extra <=> from the end. # prints x<=>y<=>z }}} Or using array slicing, described in the next section. {{{#!highlight bash # Bash/ksh typeset -a a=([0]=x [5]=y [10]=z) printf '%s<=>' "${a[@]::${#a[@]}-1}" printf '%s\n' "${a[@]:(-1)}" }}} This also shows how sparse arrays can be assigned multiple elements at once. Note using the `arr=([key]=value ...)` notation differs between shells. In ksh93, this syntax gives you an associative array by default unless you specify otherwise, and using it requires that every value be explicitly given an index, unlike bash, where omitted indexes begin at the previous index. This example was written in a way that's compatible between the two. BASH 3.0 added the ability to retrieve the list of index values in an array: {{{#!highlight bash # Bash 3.0 or higher arr=(0 1 2 3) arr[42]='what was the question?' unset -v 'arr[2]' echo "${!arr[@]}" # prints 0 1 3 42 }}} Retrieving the indices is extremely important for certain kinds of tasks, such as maintaining parallel arrays with the same indices (a cheap way to mimic having an array of `struct`s in a language with no `struct`): {{{#!highlight bash # Bash 3.0 or higher unset -v file title artist i for f in ./*.mp3; do file[i]=$f title[i]=$(mp3info -p %t "$f") artist[i++]=$(mp3info -p %a "$f") done # Later, iterate over every song. # This works even if the arrays are sparse, just so long as they all have # the SAME holes. for i in "${!file[@]}"; do echo "${file[i]} is ${title[i]} by ${artist[i]}" done }}} ==== Retrieving with modifications ==== Bash's [[BashFAQ/073|Parameter Expansions]] may be performed on array elements ''en masse'': {{{#!highlight bash # Bash arr=(abc def ghi jkl) echo "${arr[@]#?}" # prints bc ef hi kl echo "${arr[@]/[aeiou]/}" # prints bc df gh jkl }}} Parameter Expansion can also be used to extract sub-lists of elements from an array. Some people call this ''slicing'': {{{#!highlight bash # Bash echo "${arr[@]:1:3}" # three elements starting at #1 (second element) echo "${arr[@]:(-2)}" # last two elements }}} The same goes for positional parameters {{{#!highlight bash set -- foo bar baz echo "${@:(-1)}" # last positional parameter baz echo "${@:(-2):1}" # second-to-last positional parameter bar }}} === Using @ as a pseudo-array === As we see above, the `@` array (the array of positional parameters) can be used almost like a regularly named array. This is the ''only'' array available for use in POSIX or Bourne shells. It has certain limitations: you cannot individually set or unset single elements, and it cannot be sparse. Nevertheless, it still makes certain POSIX shell tasks possible that would otherwise require external tools: {{{#!highlight bash # POSIX set -- *.mp3 if [ -e "$1" ] || [ -L "$1" ]; then echo "there are $# MP3 files" else echo "there are 0 MP3 files" fi }}} {{{#!highlight bash # POSIX ... # Add an option to our dynamically generated list of options set -- "$@" -f "$somefile" ... foocommand "$@" }}} (Compare to [[BashFAQ/050|FAQ #50]]'s dynamically generated commands using named arrays.) == See Also == * [[http://wiki.bash-hackers.org/syntax/arrays|Bash-hackers array documentation]] * [[BashGuide/Arrays]] * [[BashSheet#Arrays|BashSheet Array reference]] * [[BashFAQ/006#Associative_Arrays|BashFAQ 6 - explaining associative arrays]] ---- CategoryShell