Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
syntax:arrays [2013/03/02 08:02]
ormaaj Add a bugs section, rewrite the intro, add a "referencing" section, general edits.
syntax:arrays [2018/08/22 06:51] (current)
ebersphi
Line 20: Line 20:
 Bash supports two different types of ksh-like one-dimensional arrays. **Multidimensional arrays are not implemented**. 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 [[syntax:​arith_expr | 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 ''​-a''​ attribute.   * //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 [[syntax:​arith_expr | 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 ''​-a''​ attribute.
-  * :V4: //​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 ''​-A''​ attribute, and unlike indexed arrays, Bash requires that they always be declared explicitly (as indexed arrays are the default, see [[#​Declaration | 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.+  * //​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 ''​-A''​ attribute, and unlike indexed arrays, Bash requires that they always be declared explicitly (as indexed arrays are the default, see [[#​Declaration | 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.
  
 ===== Syntax ===== ===== Syntax =====
Line 48: Line 48:
 |''​ARRAY[0]=''​ |Generally sets the first element of an **indexed** array. If no array ''​ARRAY''​ existed before, it is created. | |''​ARRAY[0]=''​ |Generally sets the first element of an **indexed** array. If no array ''​ARRAY''​ existed before, it is created. |
 |''​declare -a ARRAY''​ |Declares an **indexed** array ''​ARRAY''​. An existing array is not initialized. | |''​declare -a ARRAY''​ |Declares an **indexed** array ''​ARRAY''​. An existing array is not initialized. |
-|''​declare -A ARRAY''​ |:V4: Declares an **associative** array ''​ARRAY''​. This is the one and only way to create associative arrays. |+|''​declare -A ARRAY''​ |Declares an **associative** array ''​ARRAY''​. This is the one and only way to create associative arrays. | 
 + 
 +As an example, and for use below, let's declare our ''​NAMES''​ array as described [[#​purpose|above]]:​ 
 + 
 +    declare -a NAMES=('​Peter'​ '​Anna'​ '​Greg'​ '​Jan'​)
  
 ==== Storing values ==== ==== Storing values ====
Line 54: Line 58:
 Storing values in arrays is quite as simple as storing values in normal variables. Storing values in arrays is quite as simple as storing values in normal variables.
  
-^Syntax ^Description ^ +^ Syntax ​                            ​^ Description ​                                                                                                                                                                                                                                                                                                                                                                                                                                              ​
-|''​ARRAY[N]=VALUE''​ |Sets the element ''​N''​ of the **indexed** array ''​ARRAY''​ to ''​VALUE''​. **''​N''​ can be any valid [[syntax:​arith_expr | arithmetic expression]]**. | +| ''​ARRAY[N]=VALUE'' ​                ​| Sets the element ''​N''​ of the **indexed** array ''​ARRAY''​ to ''​VALUE''​. **''​N''​ can be any valid [[syntax:​arith_expr| arithmetic expression]]**. ​                                                                                                                                                                                                                                                                                                         
-|''​ARRAY[STRING]=VALUE''​ |:V4: Sets the element indexed by ''​STRING''​ of the **associative array** ''​ARRAY''​. | +| ''​ARRAY[STRING]=VALUE'' ​           | Sets the element indexed by ''​STRING''​ of the **associative array** ''​ARRAY''​. ​                                                                                                                                                                                                                                                                                                                                                                           
-|''​ARRAY=VALUE''​ |As above. If no index is given, as a default the zeroth element is set to ''​VALUE''​. 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"​. | +| ''​ARRAY=VALUE'' ​                   | As above. If no index is given, as a default the zeroth element is set to ''​VALUE''​. 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"​. ​                                                                                                                                                                                                                         
-|''​ARRAY=(E1\ E2\ ...)''​ | Compound array assignment - sets the whole array ''​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 (''​ARRAY=()''​),​ the array will be set to an empty array. ​:V4: This method obviously does not use explicit indexes. An **associative array** can **not** be set like that! Clearing an associative array using ''​ARRAY=()''​ works. | +| ''​ARRAY=(E1\ E2\ ...)'' ​           | Compound array assignment - sets the whole array ''​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 (''​ARRAY=()''​),​ 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 ''​ARRAY=()''​ works. ​ 
-|''​ARRAY=([X]=E1\ [Y]=E2\ ...)''​ |Compound assignment for indexed arrays with index-value pairs declared individually (here for example ''​X''​ and ''​Y''​). 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. | +| ''​ARRAY=([X]=E1\ [Y]=E2\ ...)'' ​   | Compound assignment for indexed arrays with index-value pairs declared individually (here for example ''​X''​ and ''​Y''​). 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.                                                                                           ​
-|''​ARRAY=([S1]=E1\ [S2]=E2\ ...)''​ |:V4: Individual mass-setting for **associative arrays**. The named indexes (here: ''​S1''​ and ''​S2''​) are strings. | +| ''​ARRAY=([S1]=E1\ [S2]=E2\ ...)'' ​ | Individual mass-setting for **associative arrays**. The named indexes (here: ''​S1''​ and ''​S2''​) are strings. ​                                                                                                                                                                                                                                                                                                                                             
-|''​ARRAY+=(E1\ E2\ ...)''​ |Append to ARRAY. |+| ''​ARRAY+=(E1\ E2\ ...)'' ​          ​| Append to ARRAY. ​                                                                                                                                                                                                                                                                                                                                                                                                                                         | 
 +| ''​ARRAY=("​${ANOTHER_ARRAY[@]}"​)'' ​ | Copy ANOTHER_ARRAY to ARRAY, copying each element. ​                                                                                                                                                                                                                                                                                                                                                                                                       ​|
  
 As of now, arrays can't be exported. As of now, arrays can't be exported.
Line 70: Line 75:
  
 ^Syntax ^Description ^ ^Syntax ^Description ^
-| ''​${ARRAY[N]}''​ | Expands to the value of the index ''​N''​ in the **indexed** array ''​ARRAY''​. If ''​N''​ is a negative number, it's treated as the offset from the maximum assigned index (can't be used for assignment) - 1 /:V4: 4.2-alpha) ​+| ''​${ARRAY[N]}''​ | Expands to the value of the index ''​N''​ in the **indexed** array ''​ARRAY''​. If ''​N''​ is a negative number, it's treated as the offset from the maximum assigned index (can't be used for assignment) - 1  
-| ''​${ARRAY[S]}''​ | :V4: Expands to the value of the index ''​S''​ in the **associative** array ''​ARRAY''​. |+| ''​${ARRAY[S]}''​ | Expands to the value of the index ''​S''​ in the **associative** array ''​ARRAY''​. |
 | ''"​${ARRAY[@]}"​\\ ${ARRAY[@]}\\ "​${ARRAY[*]}"​\\ ${ARRAY[*]}''​ | Similar to [[scripting:​posparams#​mass_usage| 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. | | ''"​${ARRAY[@]}"​\\ ${ARRAY[@]}\\ "​${ARRAY[*]}"​\\ ${ARRAY[*]}''​ | Similar to [[scripting:​posparams#​mass_usage| 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. |
 | ''"​${ARRAY[@]:​N:​M}"​\\ ${ARRAY[@]:​N:​M}\\ "​${ARRAY[*]:​N:​M}"​\\ ${ARRAY[*]:​N:​M}''​ | Similar to what this syntax does for the characters of a single string when doing [[syntax:​pe#​substring_expansion| substring expansion]],​ this expands to ''​M''​ elements starting with element ''​N''​. 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. | | ''"​${ARRAY[@]:​N:​M}"​\\ ${ARRAY[@]:​N:​M}\\ "​${ARRAY[*]:​N:​M}"​\\ ${ARRAY[*]:​N:​M}''​ | Similar to what this syntax does for the characters of a single string when doing [[syntax:​pe#​substring_expansion| substring expansion]],​ this expands to ''​M''​ elements starting with element ''​N''​. 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. |
Line 81: Line 86:
 ^Syntax ^Description ^ ^Syntax ^Description ^
 |''​${#​ARRAY[N]}''​ |Expands to the **length** of an individual array member at index ''​N''​ (**stringlength**) | |''​${#​ARRAY[N]}''​ |Expands to the **length** of an individual array member at index ''​N''​ (**stringlength**) |
-|''​${#​ARRAY[STRING]}''​ |:V4: Expands to the **length** of an individual associative array member at index ''​STRING''​ (**stringlength**) |+|''​${#​ARRAY[STRING]}''​ | Expands to the **length** of an individual associative array member at index ''​STRING''​ (**stringlength**) |
 |''​${#​ARRAY[@]}''​\\ ''​${#​ARRAY[*]}''​|Expands to the **number of elements** in ''​ARRAY''​ | |''​${#​ARRAY[@]}''​\\ ''​${#​ARRAY[*]}''​|Expands to the **number of elements** in ''​ARRAY''​ |
 |''​${!ARRAY[@]}''​\\ ''​${!ARRAY[*]}''​|Expands to the **indexes** in ''​ARRAY''​ since BASH 3.0| |''​${!ARRAY[@]}''​\\ ''​${!ARRAY[*]}''​|Expands to the **indexes** in ''​ARRAY''​ since BASH 3.0|
Line 91: Line 96:
 |''​unset -v ARRAY''​\\ ''​unset -v ARRAY[@]''​\\ ''​unset -v ARRAY[*]''​ |Destroys a complete array | |''​unset -v ARRAY''​\\ ''​unset -v ARRAY[@]''​\\ ''​unset -v ARRAY[*]''​ |Destroys a complete array |
 |''​unset -v ARRAY[N]''​|Destroys the array element at index ''​N''​ | |''​unset -v ARRAY[N]''​|Destroys the array element at index ''​N''​ |
-|''​unset -v ARRAY[STRING]''​|:V4: Destroys the array element of the associative array at index ''​STRING''​ |+|''​unset -v ARRAY[STRING]''​|Destroys the array element of the associative array at index ''​STRING''​ |
  
 It is best to [[commands/​builtin/​unset#​portability_considerations | explicitly specify -v]] when unsetting variables with unset. It is best to [[commands/​builtin/​unset#​portability_considerations | explicitly specify -v]] when unsetting variables with unset.
Line 147: Line 152:
 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//"​). 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//"​).
  
 +Now, suppose that you want to replace your array ''​sentence''​ with the values in the [[#​purpose|previously-declared array]] ''​NAMES''​ . You might think you could just do
 +
 +<​code>​
 +$ unset sentence ; declare -a sentence=NAMES
 +$ echo ${#​sentence[@]}
 +1
 +# omit calculating max_index as above, and iterate as one-liner
 +$ for ((i = 0; i < ${#​sentence[@]};​ i++)); do  echo "​Element $i: '​${sentence[i]}'"​ ; done
 +Element 0: '​NAMES'​
 +</​code>​
 +
 +Obviously that's wrong. What about
 +
 +<​code>​
 +$ unset sentence ; declare -a sentence=${NAMES}
 +</​code>​
 +
 +? Again, wrong:
 +
 +<​code>​
 +$ echo ${#​sentence[*]}
 +1
 +$ for ((i = 0; i < ${#​sentence[@]};​ i++)); do  echo "​Element $i: '​${sentence[i]}'"​ ; done
 +Element 0: '​Peter'​
 +</​code>​
 +
 +So what's the **right** way? The (slightly ugly) answer is, reuse the enumeration syntax:
 +
 +<​code>​
 +$ unset sentence ; declare -a sentence=("​${NAMES[@]}"​)
 +$ echo ${#​sentence[@]}
 +4
 +$ for ((i = 0; i < ${#​sentence[@]};​ i++)); do  echo "​Element $i: '​${sentence[i]}'"​ ; done
 +Element 0: '​Peter'​
 +Element 1: '​Anna'​
 +Element 2: '​Greg'​
 +Element 3: '​Jan'​
 +</​code>​
  
 ==== Associative (Bash 4) ==== ==== Associative (Bash 4) ====
Line 290: Line 333:
   * Arrays are not specified by POSIX. One-dimensional indexed arrays are supported using similar syntax and semantics by most Korn-like shells.   * 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 ''​typeset -A''​ in Bash 4, Zsh, and Ksh93.   * Associative arrays are supported via ''​typeset -A''​ 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 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. <​code>​+  * 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 ''​a=(...)''​ style compound assignment without specifying indexes is an error. I can't explain this inconsistency.<​code>​
  $ 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.  $ 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) 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.  $ 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) 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
 </​code>​ </​code>​
   * 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.   * 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.
Line 334: Line 379:
   * ksh93 also has an additional slice notation: ''"​${arr[n..m]}"''​ where ''​n''​ and ''​m''​ are arithmetic expressions. These are needed for use with multi-dimensional arrays.   * ksh93 also has an additional slice notation: ''"​${arr[n..m]}"''​ where ''​n''​ and ''​m''​ 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 ''​UINT_MAX'',​ which would be addressed by ''​arr[-1]''​.   * Assigning or referencing negative indexes in mksh causes wrap-around. The max index appears to be ''​UINT_MAX'',​ which would be addressed by ''​arr[-1]''​.
-  * So far, Bash's ''​[[ -v var ]]''​ 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 ''​-v''​ supports both. Other shells lack a ''​-v''​ test.+  * So far, Bash's ''​-v var''​ 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 ''​-v''​ supports both. Other shells lack a ''​-v''​ test.
  
 ==== Bugs ==== ==== Bugs ====
  
-  * 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. <​code>​+  ​* **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. <​code>​
 $ touch '​[1]=a';​ bash -c '​a=([1]=*);​ echo "​${a[@]}"'​ $ touch '​[1]=a';​ bash -c '​a=([1]=*);​ echo "​${a[@]}"'​
 [1]=a [1]=a
Line 345: Line 390:
 1=a 1=a
 </​code>​ </​code>​
-  * In addition to the above globbing issue, assignments preceding "​declare"​ have an additional effect on brace and pathname expansion. <​code>​+  ​* **Fixed in 4.3** In addition to the above globbing issue, assignments preceding "​declare"​ have an additional effect on brace and pathname expansion. <​code>​
 $ set -x; foo=bar declare arr=( {1..10} ) $ set -x; foo=bar declare arr=( {1..10} )
 + foo=bar + foo=bar
-+ declare 'a=(1)' 'a=(2)' 'a=(3)' 'a=(4)' 'a=(5)'++ declare 'arr=(1)' 'arr=(2)' 'arr=(3)' 'arr=(4)' 'arr=(5)' '​arr=(6)'​ '​arr=(7)'​ '​arr=(8)'​ '​arr=(9)'​ '​arr=(10)'
  
 $ touch xy=foo $ touch xy=foo
 ++ touch xy=foo
 $ declare x[y]=* $ declare x[y]=*
 + declare '​x[y]=*'​ + declare '​x[y]=*'​
Line 357: Line 403:
 + declare xy=foo + declare xy=foo
 </​code>​ Each word (the entire assignment) is subject to globbing and brace expansion. This appears to trigger the same strange expansion mode as ''​let'',​ ''​eval'',​ other declaration commands, and maybe more.  </​code>​ Each word (the entire assignment) is subject to globbing and brace expansion. This appears to trigger the same strange expansion mode as ''​let'',​ ''​eval'',​ other declaration commands, and maybe more. 
-  * Indirection combined with another modifier expands arrays to a single word. <​code>​+  ​* **Fixed in 4.3** Indirection combined with another modifier expands arrays to a single word. <​code>​
 $ a=({a..c}) b=a[@]; printf '<​%s>​ ' "​${!b}";​ echo; printf '<​%s>​ ' "​${!b/​%/​foo}";​ echo $ a=({a..c}) b=a[@]; printf '<​%s>​ ' "​${!b}";​ echo; printf '<​%s>​ ' "​${!b/​%/​foo}";​ echo
 <a> <b> <c> <a> <b> <c>
 <a b cfoo> <a b cfoo>
 </​code>​ </​code>​
-  * Process substitutions are evaluated within array indexes. Zsh and ksh don't do this in any arithmetic ​contextT. <​code> ​+  ​* **Fixed in 4.3** Process substitutions are evaluated within array indexes. Zsh and ksh don't do this in any arithmetic ​context. <​code> ​
 # print "​moo"​ # print "​moo"​
 dev=fd=1 _[1<​(echo moo >&​2)]= dev=fd=1 _[1<​(echo moo >&​2)]=
  • syntax/arrays.1362211352.txt
  • Last modified: 2013/03/02 08:02
  • by ormaaj