Shell Script Array Assignment Operator

An array is a variable containing multiple values may be of same type or of different type.  There is no maximum limit to the size of an array, nor any requirement that member variables be indexed or assigned contiguously. Array index starts with zero.

In this article, let us review 15 various array operations in bash.

This article is part of the on-going Bash Tutorial series. For those who are new to bash scripting, get a jump-start from the Bash Scripting Introduction tutorial.

1. Declaring an Array and Assigning values

In bash, array is created automatically when a variable is used in the format like,

name[index]=value
  • name is any name for an array
  • index could be any number or expression that must evaluate to a number greater than or equal to zero.You can declare an explicit array using declare -a arrayname.
$ cat arraymanip.sh #! /bin/bash Unix[0]='Debian' Unix[1]='Red hat' Unix[2]='Ubuntu' Unix[3]='Suse' echo ${Unix[1]} $./arraymanip.sh Red hat

To access an element from an array use curly brackets like ${name[index]}.

2. Initializing an array during declaration

Instead of initializing an each element of an array separately, you can declare and initialize an array by specifying the list of elements (separated by white space) with in a curly braces.

Syntax: declare -a arrayname=(element1 element2 element3)

If the elements has the white space character, enclose it with in a quotes.

#! /bin/bash $cat arraymanip.sh declare -a Unix=('Debian' 'Red hat' 'Red hat' 'Suse' 'Fedora');

declare -a declares an array and all the elements in the parentheses are the elements of an array.

3. Print the Whole Bash Array

There are different ways to print the whole elements of the array. If the index number is @ or *, all members of an array are referenced. You can traverse through the array elements and print it, using looping statements in bash.

echo ${Unix[@]} # Add the above echo statement into the arraymanip.sh #./t.sh Debian Red hat Ubuntu Suse

Referring to the content of a member variable of an array without providing an index number is the same as referring to the content of the first element, the one referenced with index number zero.

4. Length of the Bash Array

We can get the length of an array using the special parameter called $#.

${#arrayname[@]} gives you the length of the array.

$ cat arraymanip.sh declare -a Unix=('Debian' 'Red hat' 'Suse' 'Fedora'); echo ${#Unix[@]} #Number of elements in the array echo ${#Unix} #Number of characters in the first element of the array.i.e Debian $./arraymanip.sh 4 6

5. Length of the nth Element in an Array

${#arrayname[n]} should give the length of the nth element in an array.

$cat arraymanip.sh #! /bin/bash Unix[0]='Debian' Unix[1]='Red hat' Unix[2]='Ubuntu' Unix[3]='Suse' echo ${#Unix[3]} # length of the element located at index 3 i.e Suse $./arraymanip.sh 4

6. Extraction by offset and length for an array

The following example shows the way to extract 2 elements starting from the position 3 from an array called Unix.

$cat arraymanip.sh Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); echo ${Unix[@]:3:2} $./arraymanip.sh Suse Fedora

The above example returns the elements in the 3rd index and fourth index. Index always starts with zero.

7. Extraction with offset and length, for a particular element of an array

To extract only first four elements from an array element . For example, Ubuntu which is located at the second index of an array, you can use offset and length for a particular element of an array.

$cat arraymanip.sh #! /bin/bash Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); echo ${Unix[2]:0:4} ./arraymanip.sh Ubun

The above example extracts the first four characters from the 2nd indexed element of an array.

8. Search and Replace in an array elements

The following example, searches for Ubuntu in an array elements, and replace the same with the word ‘SCO Unix’.

$cat arraymanip.sh #!/bin/bash Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); echo ${Unix[@]/Ubuntu/SCO Unix} $./arraymanip.sh Debian Red hat SCO Unix Suse Fedora UTS OpenLinux

In this example, it replaces the element in the 2nd index ‘Ubuntu’ with ‘SCO Unix’. But this example will not permanently replace the array content.

9. Add an element to an existing Bash Array

The following example shows the way to add an element to the existing array.

$cat arraymanip.sh Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); Unix=("${Unix[@]}" "AIX" "HP-UX") echo ${Unix[7]} $./arraymanip.sh AIX

In the array called Unix, the elements ‘AIX’ and ‘HP-UX’ are added in 7th and 8th index respectively.

10. Remove an Element from an Array

unset is used to remove an element from an array.unset will have the same effect as assigning null to an element.

$cat arraymanip.sh #!/bin/bash Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); unset Unix[3] echo ${Unix[3]}

The above script will just print null which is the value available in the 3rd index. The following example shows one of the way to remove an element completely from an array.

$ cat arraymanip.sh Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); pos=3 Unix=(${Unix[@]:0:$pos} ${Unix[@]:$(($pos + 1))}) echo ${Unix[@]} $./arraymanip.sh Debian Red hat Ubuntu Fedora UTS OpenLinux

In this example, ${Unix[@]:0:$pos} will give you 3 elements starting from 0th index i.e 0,1,2 and ${Unix[@]:4} will give the elements from 4th index to the last index. And merge both the above output. This is one of the workaround to remove an element from an array.

11. Remove Bash Array Elements using Patterns

In the search condition you can give the patterns, and stores the remaining element to an another array as shown below.

$ cat arraymanip.sh #!/bin/bash declare -a Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora'); declare -a patter=( ${Unix[@]/Red*/} ) echo ${patter[@]} $ ./arraymanip.sh Debian Ubuntu Suse Fedora

The above example removes the elements which has the patter Red*.

12. Copying an Array

Expand the array elements and store that into a new array as shown below.

#!/bin/bash Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); Linux=("${Unix[@]}") echo ${Linux[@]} $ ./arraymanip.sh Debian Red hat Ubuntu Fedora UTS OpenLinux

13. Concatenation of two Bash Arrays

Expand the elements of the two arrays and assign it to the new array.

$cat arraymanip.sh #!/bin/bash Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); Shell=('bash' 'csh' 'jsh' 'rsh' 'ksh' 'rc' 'tcsh'); UnixShell=("${Unix[@]}" "${Shell[@]}") echo ${UnixShell[@]} echo ${#UnixShell[@]} $ ./arraymanip.sh Debian Red hat Ubuntu Suse Fedora UTS OpenLinux bash csh jsh rsh ksh rc tcsh 14

It prints the array which has the elements of the both the array ‘Unix’ and ‘Shell’, and number of elements of the new array is 14.

14. Deleting an Entire Array

unset is used to delete an entire array.

$cat arraymanip.sh #!/bin/bash Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux'); Shell=('bash' 'csh' 'jsh' 'rsh' 'ksh' 'rc' 'tcsh'); UnixShell=("${Unix[@]}" "${Shell[@]}") unset UnixShell echo ${#UnixShell[@]} $ ./arraymanip.sh 0

After unset an array, its length would be zero as shown above.

15. Load Content of a File into an Array

You can load the content of the file line by line into an array.

#Example file $ cat logfile Welcome to thegeekstuff Linux Unix $ cat loadcontent.sh #!/bin/bash filecontent=( `cat "logfile" `) for t in "${filecontent[@]}" do echo $t done echo "Read file content!" $ ./loadcontent.sh Welcome to thegeekstuff Linux Unix Read file content!

In the above example, each index of an array element has printed through for loop.

Recommended Reading

Bash 101 Hacks, by Ramesh Natarajan. I spend most of my time on Linux environment. So, naturally I’m a huge fan of Bash command line and shell scripting. 15 years back, when I was working on different flavors of *nix, I used to write lot of code on C shell and Korn shell. Later years, when I started working on Linux as system administrator, I pretty much automated every possible task using Bash shell scripting. Based on my Bash experience, I’ve written Bash 101 Hacks eBook that contains 101 practical examples on both Bash command line and shell scripting. If you’ve been thinking about mastering Bash, do yourself a favor and read this book, which will help you take control of your Bash command line and shell scripting.

If you enjoyed this article, you might also like..



Tagged as:Bash Array String, Bash Arrays, Bash Script Array, Bash Scripting Tutorial, Bash Tutorial, Echo Array, Linux Array, Unix Array

An array is a parameter that holds mappings from keys to values. Arrays are used to store a collection of parameters into a parameter. Arrays (in any programming language) are a useful and common composite data structure, and one of the most important scripting features in Bash and other shells.

Here is an abstract representation of an array named . The indexes go from 0 to 3.

NAMES 0: Peter 1: Anna 2: Greg 3: Jan

Instead of using 4 separate variables, multiple related variables are grouped grouped together into elements of the array, accessible by their key. If you want the second name, ask for index 1 of the array .

Bash supports two different types of ksh-like one-dimensional arrays. Multidimensional arrays are not implemented.

  • Indexed arrays use positive integer numbers as keys. Indexed arrays are always sparse, meaning indexes are not necessarily contiguous. All syntax used for both assigning and dereferencing indexed arrays is an arithmetic evaluation context (see Referencing). As in C and many other languages, the numerical array indexes start at 0 (zero). Indexed arrays are the most common, useful, and portable type. Indexed arrays were first introduced to Bourne-like shells by ksh88. Similar, partially compatible syntax was inherited by many derivatives including Bash. Indexed arrays always carry the attribute.

  • Associative arrays (sometimes known as a "hash" or "dict") use arbitrary nonempty strings as keys. In other words, associative arrays allow you to look up a value from a table based upon its corresponding string label. Associative arrays are always unordered, they merely associate key-value pairs. If you retrieve multiple values from the array at once, you can't count on them coming out in the same order you put them in. Associative arrays always carry the attribute, and unlike indexed arrays, Bash requires that they always be declared explicitly (as indexed arrays are the default, see declaration). Associative arrays were first introduced in ksh93, and similar mechanisms were later adopted by Zsh and Bash version 4. These three are currently the only POSIX-compatible shells with any associative array support.

Referencing

To accommodate referring to array variables and their individual elements, Bash extends the parameter naming scheme with a subscript suffix. Any valid ordinary scalar parameter name is also a valid array name: . The parameter name may be followed by an optional subscript enclosed in square brackets to refer to a member of the array.

The overall syntax is - where for indexed arrays, is any valid arithmetic expression, and for associative arrays, any nonempty string. Subscripts are first processed for parameter and arithmetic expansions, and command and process substitutions. When used within parameter expansions or as an argument to the unset builtin, the special subscripts and are also accepted which act upon arrays analogously to the way the and special parameters act upon the positional parameters. In parsing the subscript, bash ignores any text that follows the closing bracket up to the end of the parameter name.

With few exceptions, names of this form may be used anywhere ordinary parameter names are valid, such as within arithmetic expressions, parameter expansions, and as arguments to builtins that accept parameter names. An array is a Bash parameter that has been given the (for indexed) or (for associative) attributes. However, any regular (non-special or positional) parameter may be validly referenced using a subscript, because in most contexts, referring to the zeroth element of an array is synonymous with referring to the array name without a subscript.

# "x" is an ordinary non-array parameter. $ x=hi; printf '%s ' "$x" "${x[0]}"; echo "${_[0]}" hi hi hi

The only exceptions to this rule are in a few cases where the array variable's name refers to the array as a whole. This is the case for the builtin (see destruction) and when declaring an array without assigning any values (see declaration).

Declaration

The following explicitly give variables array attributes, making them arrays:

Syntax Description
Declares an indexed array and initializes it to be empty. This can also be used to empty an existing array.
Generally sets the first element of an indexed array. If no array existed before, it is created.
Declares an indexed array . An existing array is not initialized.
Declares an associative array . This is the one and only way to create associative arrays.

Storing values

Storing values in arrays is quite as simple as storing values in normal variables.

Syntax Description
Sets the element of the indexed array to . can be any valid arithmetic expression.
Sets the element indexed by of the associative array.
As above. If no index is given, as a default the zeroth element is set to . Careful, this is even true of associative arrays - there is no error if no key is specified, and the value is assigned to string index "0".
Compound array assignment - sets the whole array to the given list of elements indexed sequentially starting at zero. The array is unset before assignment unless the += operator is used. When the list is empty (), the array will be set to an empty array. This method obviously does not use explicit indexes. An associative array can not be set like that! Clearing an associative array using works.
Compound assignment for indexed arrays with index-value pairs declared individually (here for example and ). X and Y are arithmetic expressions. This syntax can be combined with the above - elements declared without an explicitly specified index are assigned sequentially starting at either the last element with an explicit index, or zero.
Individual mass-setting for associative arrays. The named indexes (here: and ) are strings.
Append to ARRAY.

As of now, arrays can't be exported.

Getting values

For completeness and details on several parameter expansion variants, see the article about parameter expansion and check the notes about arrays.

Syntax Description
Expands to the value of the index in the indexed array . If is a negative number, it's treated as the offset from the maximum assigned index (can't be used for assignment) - 1
Expands to the value of the index in the associative array .
Similar to mass-expanding positional parameters, this expands to all elements. If unquoted, both subscripts and expand to the same result, if quoted, expands to all elements individually quoted, expands to all elements quoted as a whole.
Similar to what this syntax does for the characters of a single string when doing substring expansion, this expands to elements starting with element . This way you can mass-expand individual indexes. The rules for quoting and the subscripts and are the same as above for the other mass-expansions.

For clarification: When you use the subscripts or for mass-expanding, then the behaviour is exactly what it is for and when mass-expanding the positional parameters. You should read this article to understand what's going on.

Metadata

Destruction

The unset builtin command is used to destroy (unset) arrays or individual elements of arrays.

Syntax Description


Destroys a complete array
Destroys the array element at index
Destroys the array element of the associative array at index

It is best to explicitly specify -v when unsetting variables with unset.

Specifying unquoted array elements as arguments to any command, such as with the syntax above may cause pathname expansion to occur due to the presence of glob characters.

Example: You are in a directory with a file named , and you want to destroy an array element , with

unset x[1] then pathname expansion will expand to the filename and break your processing!

Even worse, if is set, your array/index will disappear.

To avoid this, always quote the array name and index:

unset -v 'x[1]'

This applies generally to all commands which take variable names as arguments. Single quotes preferred.

Numerical Index

Numerical indexed arrays are easy to understand and easy to use. The Purpose and Indexing chapters above more or less explain all the needed background theory.

Now, some examples and comments for you.

Let's say we have an array which is initialized as follows:

sentence=(Be liberal in what you accept, and conservative in what you send)

Since no special code is there to prevent word splitting (no quotes), every word there will be assigned to an individual array element. When you count the words you see, you should get 12. Now let's see if Bash has the same opinion:

$ echo ${#sentence[@]} 12

Yes, 12. Fine. You can take this number to walk through the array. Just subtract 1 from the number of elements, and start your walk at 0 (zero):

((n_elements=${#sentence[@]}, max_index=n_elements - 1)) for ((i = 0; i <= max_index; i++)); do echo "Element $i: '${sentence[i]}'" done

You always have to remember that, it seems newbies have problems sometimes. Please understand that numerical array indexing begins at 0 (zero)!

The method above, walking through an array by just knowing its number of elements, only works for arrays where all elements are set, of course. If one element in the middle is removed, then the calculation is nonsense, because the number of elements doesn't correspond to the highest used index anymore (we call them "sparse arrays").

Associative (Bash 4)

Associative arrays (or hash tables) are not much more complicated than numerical indexed arrays. The numerical index value (in Bash a number starting at zero) just is replaced with an arbitrary string:

# declare -A, introduced with Bash 4 to declare an associative array declare -A sentence sentence[Begin]='Be liberal in what' sentence[Middle]='you accept, and conservative' sentence[End]='in what you send' sentence['Very end']=...

Beware: don't rely on the fact that the elements are ordered in memory like they were declared, it could look like this:

# output from 'set' command sentence=([End]="in what you send" [Middle]="you accept, and conservative " [Begin]="Be liberal in what " ["Very end"]="...") This effectively means, you can get the data back with , of course (just like with numerical indexing), but you can't rely on a specific order. If you want to store ordered data, or re-order data, go with numerical indexes. For associative arrays, you usually query known index values: for element in Begin Middle End "Very end"; do printf "%s" "${sentence[$element]}" done printf "\n"

A nice code example: Checking for duplicate files using an associative array indexed with the SHA sum of the files:

# Thanks to Tramp in #bash for the idea and the code unset flist; declare -A flist; while read -r sum fname; do if [[ ${flist[$sum]} ]]; then printf 'rm -- "%s" # Same as >%s<\n' "$fname" "${flist[$sum]}" else flist[$sum]="$fname" fi done < <(find . -type f -exec sha256sum {} +) >rmdups

Integer arrays

Any type attributes applied to an array apply to all elements of the array. If the integer attribute is set for either indexed or associative arrays, then values are considered as arithmetic for both compound and ordinary assignment, and the += operator is modified in the same way as for ordinary integer variables.

~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]="a[2]")' 'a+=(42 [a[4]]+=3)'; declare -p a ) declare -ai a='([0]="6" [2]="4" [4]="7" [5]="42")'

is assigned to the result of . gets the result of . The last index in the first assignment is the result of , which has already been assigned as , and its value is also given .

This shows that even though any existing arrays named in the current scope have already been unset by using instead of to the compound assignment, arithmetic variables within keys can self-reference any elements already assigned within the same compound-assignment. With integer arrays this also applies to expressions to the right of the . (See evaluation order, the right side of an arithmetic assignment is typically evaluated first in Bash.)

The second compound assignment argument to declare uses , so it appends after the last element of the existing array rather than deleting it and creating a new array, so gets .

Lastly, the element whose index is the value of (), gets added to its existing value, making == . Note that having the integer attribute set this time causes += to add, rather than append a string, as it would for a non-integer array.

The single quotes force the assignments to be evaluated in the environment of . This is important because attributes are only applied to the assignment after assignment arguments are processed. Without them the compound assignment would have been invalid, and strings would have been inserted into the integer array without evaluating the arithmetic. A special-case of this is shown in the next section.

Bash declaration commands are really keywords in disguise. They magically parse arguments to determine whether they are in the form of a valid assignment. If so, they are evaluated as assignments. If not, they are undergo normal argument expansion before being passed to the builtin which evaluates the resulting string as an assignment (somewhat like , but there are differences.) ' Discuss this in detail.

Indirection

Arrays can be expanded indirectly using the indirect parameter expansion syntax. Parameters whose values are of the form: , , or when expanded indirectly produce the expected results. This is mainly useful for passing arrays (especially multiple arrays) by name to a function.

This example is an "isSubset"-like predicate which returns true if all key-value pairs of the array given as the first argument to isSubset correspond to a key-value of the array given as the second argument. It demonstrates both indirect array expansion and indirect key-passing without eval using the aforementioned special compound assignment expansion.

isSubset() { local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")' set -- "${@/%/[key]}" (( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1 local key for key in "${xkeys[@]}"; do [[ ${!2+_} && ${!1} == ${!2} ]] || return 1 done } main() { # "a" is a subset of "b" local -a 'a=({0..5})' 'b=({0..10})' isSubset a b echo $? # true # "a" contains a key not in "b" local -a 'a=([5]=5 {6..11})' 'b=({0..10})' isSubset a b echo $? # false # "a" contains an element whose value != the corresponding member of "b" local -a 'a=([5]=5 6 8 9 10)' 'b=({0..10})' isSubset a b echo $? # false } main

This script is one way of implementing a crude multidimensional associative array by storing array definitions in an array and referencing them through indirection. The script takes two keys and dynamically calls a function whose name is resolved from the array.

callFuncs() { # Set up indirect references as positional parameters to minimize local name collisions. set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'} # The only way to test for set but null parameters is unfortunately to test each individually. local x for x; do [[ $x ]] || return 0 done local -A a=( [foo]='([r]=f [s]=g [t]=h)' [bar]='([u]=i [v]=j [w]=k)' [baz]='([x]=l [y]=m [z]=n)' ) ${4+${a["$1"]+"${1}=${!3}"}} # For example, if "$1" is "bar" then define a new array: bar=([u]=i [v]=j [w]=k) ${4+${a["$1"]+"${!4-:}"}} # Now just lookup the new array. for inputs: "bar" "v", the function named "j" will be called, which prints "j" to stdout. } main() { # Define functions named {f..n} which just print their own names. local fun='() { echo "$FUNCNAME"; }' x for x in {f..n}; do eval "${x}${fun}" done callFuncs "$@" } main "$@"

Bugs and Portability Considerations

  • Arrays are not specified by POSIX. One-dimensional indexed arrays are supported using similar syntax and semantics by most Korn-like shells.

  • Associative arrays are supported via in Bash 4, Zsh, and Ksh93.

  • In Ksh93, arrays whose types are not given explicitly are not necessarily indexed. Arrays defined using compound assignments which specify subscripts are associative by default. In Bash, associative arrays can only be created by explicitly declaring them as associative, otherwise they are always indexed. In addition, ksh93 has several other compound structures whose types can be determined by the compound assignment syntax used to create them.

  • In Ksh93, using the compound assignment operator unsets the array, including any attributes that have been set on the array prior to assignment. In order to preserve attributes, you must use the operator. However, declaring an associative array, then attempting an style compound assignment without specifying indexes is an error. I can't explain this inconsistency. $ ksh -c 'function f { typeset -a a; a=([0]=foo [1]=bar); typeset -p a; }; f' # Attribute is lost, and since subscripts are given, we default to associative. typeset -A a=([0]=foo [1]=bar) $ ksh -c 'function f { typeset -a a; a+=([0]=foo [1]=bar); typeset -p a; }; f' # Now using += gives us the expected results. typeset -a a=(foo bar) $ ksh -c 'function f { typeset -A a; a=(foo bar); typeset -p a; }; f' # On top of that, the reverse does NOT unset the attribute. No idea why. ksh: f: line 1: cannot append index array to associative array a
  • Only Bash and mksh support compound assignment with mixed explicit subscripts and automatically incrementing subscripts. In ksh93, in order to specify individual subscripts within a compound assignment, all subscripts must be given (or none). Zsh doesn't support specifying individual subscripts at all.

  • Appending to a compound assignment is a fairly portable way to append elements after the last index of an array. In Bash, this also sets append mode for all individual assignments within the compound assignment, such that if a lower subscript is specified, subsequent elements will be appended to previous values. In ksh93, it causes subscripts to be ignored, forcing appending everything after the last element. (Appending has different meaning due to support for multi-dimensional arrays and nested compound datastructures.) $ ksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=([3]=blah [0]=bork [1]=blarg [2]=zooj); typeset -p a; }; f' # ksh93 forces appending to the array, disregarding subscripts typeset -a a=(foo bar baz '[3]=blah' '[0]=bork' '[1]=blarg' '[2]=zooj') $ bash -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Bash applies += to every individual subscript. declare -a a='([0]="foobork" [1]="barblarg" [2]="bazzooj" [3]="blah")' $ mksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Mksh does like Bash, but clobbers previous values rather than appending. set -A a typeset a[0]=bork typeset a[1]=blarg typeset a[2]=zooj typeset a[3]=blah
  • In Bash and Zsh, the alternate value assignment parameter expansion () evaluates the subscript twice, first to determine whether to expand the alternate, and second to determine the index to assign the alternate to. See evaluation order. $ : ${_[$(echo $RANDOM >&2)1]:=$(echo hi >&2)} 13574 hi 14485
  • In Zsh, arrays are indexed starting at 1 in its default mode. Emulation modes are required in order to get any kind of portability.

  • Zsh and mksh do not support compound assignment arguments to .

  • Ksh88 didn't support modern compound array assignment syntax. The original (and most portable) way to assign multiple elements is to use the syntax. This is supported by almost all shells that support ksh-like arrays except for Bash. Additionally, these shells usually support an optional argument to which performs lexicographic sorting on either array elements or the positional parameters. Bash has no built-in sorting ability other than the usual comparison operators. $ ksh -c 'set -A arr -- foo bar bork baz; typeset -p arr' # Classic array assignment syntax typeset -a arr=(foo bar bork baz) $ ksh -c 'set -sA arr -- foo bar bork baz; typeset -p arr' # Native sorting! typeset -a arr=(bar baz bork foo) $ mksh -c 'set -sA arr -- foo "[3]=bar" "[2]=baz" "[7]=bork"; typeset -p arr' # Probably a bug. I think the maintainer is aware of it. set -A arr typeset arr[2]=baz typeset arr[3]=bar typeset arr[7]=bork typeset arr[8]=foo
  • Evaluation order for assignments involving arrays varies significantly depending on context. Notably, the order of evaluating the subscript or the value first can change in almost every shell for both expansions and arithmetic variables. See evaluation order for details.

  • Bash 4.1.* and below cannot use negative subscripts to address array indexes relative to the highest-numbered index. You must use the subscript expansion, i.e. , to expand the nth-last element (or the next-highest indexed after if is unset). In Bash 4.2, you may expand (but not assign to) a negative index. In Bash 4.3, ksh93, and zsh, you may both assign and expand negative offsets.

  • ksh93 also has an additional slice notation: where and are arithmetic expressions. These are needed for use with multi-dimensional arrays.

  • Assigning or referencing negative indexes in mksh causes wrap-around. The max index appears to be , which would be addressed by .

  • So far, Bash's test doesn't support individual array subscripts. You may supply an array name to test whether an array is defined, but can't check an element. ksh93's supports both. Other shells lack a test.

Bugs

  • Fixed in 4.3 Bash 4.2.* and earlier considers each chunk of a compound assignment, including the subscript for globbing. The subscript part is considered quoted, but any unquoted glob characters on the right-hand side of the will be clumped with the subscript and counted as a glob. Therefore, you must quote anything on the right of the sign. This is fixed in 4.3, so that each subscript assignment statement is expanded following the same rules as an ordinary assignment. This also works correctly in ksh93. $ touch '[1]=a'; bash -c 'a=([1]=*); echo "${a[@]}"' [1]=a mksh has a similar but even worse problem in that the entire subscript is considered a glob. $ touch 1=a; mksh -c 'a=([123]=*); print -r -- "${a[@]}"' 1=a
  • Fixed in 4.3 In addition to the above globbing issue, assignments preceding "declare" have an additional effect on brace and pathname expansion. $ set -x; foo=bar declare arr=( {1..10} ) + foo=bar + declare 'a=(1)' 'a=(2)' 'a=(3)' 'a=(4)' 'a=(5)' $ touch xy=foo $ declare x[y]=* + declare 'x[y]=*' $ foo=bar declare x[y]=* + foo=bar + declare xy=foo Each word (the entire assignment) is subject to globbing and brace expansion. This appears to trigger the same strange expansion mode as , , other declaration commands, and maybe more.
  • Fixed in 4.3 Indirection combined with another modifier expands arrays to a single word. $ a=({a..c}) b=a[@]; printf '<%s> ' "${!b}"; echo; printf '<%s> ' "${!b/%/foo}"; echo <a> <b> <c> <a b cfoo>
  • Fixed in 4.3 Process substitutions are evaluated within array indexes. Zsh and ksh don't do this in any arithmetic context. # print "moo" dev=fd=1 _[1<(echo moo >&2)]= # Fork bomb ${dev[${dev='dev[1>(${dev[dev]})]'}]}

Evaluation order

Here are some of the nasty details of array assignment evaluation order. You can use this testcase code to generate these results.

Each testcase prints evaluation order for indexed array assignment contexts. Each context is tested for expansions (represented by digits) and arithmetic (letters), ordered from left to right within the expression. The output corresponds to the way evaluation is re-ordered for each shell: a[ $1 a ]=${b[ $2 b ]:=${c[ $3 c ]}} No attributes a[ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} typeset -ia a a[ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} typeset -ia b a[ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} typeset -ia a b (( a[ $1 a ] = b[ $2 b ] ${c[ $3 c ]} )) No attributes (( a[ $1 a ] = ${b[ $2 b ]:=c[ $3 c ]} )) typeset -ia b a+=( [ $1 a ]=${b[ $2 b ]:=${c[ $3 c ]}} [ $4 d ]=$(( $5 e )) ) typeset -a a a+=( [ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} [ $4 d ]=${5}e ) typeset -ia a bash: 4.2.42(1)-release 2 b 3 c 2 b 1 a 2 b 3 2 b 1 a c 2 b 3 2 b c 1 a 2 b 3 2 b c 1 a c 1 2 3 c b a 1 2 b 3 2 b c c a 1 2 b 3 c 2 b 4 5 e a d 1 2 b 3 2 b 4 5 a c d e ksh93: Version AJM 93v- 2013-02-22 1 2 b b a 1 2 b b a 1 2 b b a 1 2 b b a 1 2 3 c b a 1 2 b b a 1 2 b b a 4 5 e d 1 2 b b a 4 5 d e mksh: @(#)MIRBSD KSH R44 2013/02/24 2 b 3 c 1 a 2 b 3 1 a c 2 b 3 c 1 a 2 b 3 c 1 a 1 2 3 c a b 1 2 b 3 c a 1 2 b 3 c 4 5 e a d 1 2 b 3 4 5 a c d e zsh: 5.0.2 2 b 3 c 2 b 1 a 2 b 3 2 b 1 a c 2 b 1 a 2 b 1 a 1 2 3 c b a 1 2 b a 1 2 b 3 c 2 b 4 5 e 1 2 b 3 2 b 4 5
Syntax Description
Expands to the length of an individual array member at index (stringlength)
Expands to the length of an individual associative array member at index (stringlength)

Expands to the number of elements in

Expands to the indexes in since BASH 3.0

vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=dokuwiki et wrap lbr:

0 thoughts on “Shell Script Array Assignment Operator

Leave a Reply

Your email address will not be published. Required fields are marked *