Links

27. 数组

新版本的Bash支持一维数组。 数组元素可以使用符号variable[xx] 来初始化。另外,脚本可以使用declare -a variable语句来指定一个数组。 如果想引用一个数组元素(也就是取值),可以使用大括号,访问形式为 ${element[xx]} 。
例子 27-1. 简单的数组使用
#!/bin/bash
area[11]=23
area[13]=37
area[51]=UFOs
# 数组成员不一定非得是相邻或连续的。
# 数组的部分成员可以不被初始化。
# 数组中允许空缺元素。
# 实际上,保存着稀疏数据的数组(“稀疏数组”)
#+ 在电子表格处理软件中是非常有用的。
echo -n "area[11] = "
echo ${area[11]} # 需要{大括号}。
echo -n "area[13] = "
echo ${area[13]}
echo "Contents of area[51] are ${area[51]}."
# 没被初始化的数组成员打印为空值(null变量)。
echo -n "area[43] = "
echo ${area[43]}
echo "(area[43] unassigned)"
echo
# 两个数组元素的和被赋值给另一个数组元素。
area[5]=`expr ${area[11]} + ${area[13]}`
echo "area[5] = area[11] + area[13]"
echo -n "area[5] = "
echo ${area[5]}
area[6]=`expr ${area[11]} + ${area[51]}`
echo "area[6] = area[11] + area[51]"
echo -n "area[6] = "
echo ${area[6]}
# 这里会失败,是因为不允许整数与字符串相加。
echo; echo; echo
# -----------------------------------------------------------------
# 另一个数组, "area2".
# 另一种给数组变量赋值的方法...
# array_name=( XXX YYY ZZZ ... )
area2=( zero one two three four )
echo -n "area2[0] = "
echo ${area2[0]}
# 啊哈,从0开始计算数组下标(也就是,数组的第一个元素为[0],而不是[1]).
echo -n "area2[1] = "
echo ${area2[1]} # [1] 是数组的第二个元素。
# -----------------------------------------------------------------
echo; echo; echo
# -----------------------------------------------
# 第三个数组, "area3".
# 另外一种给数组元素赋值的方法...
# array_name=([xx]=XXX [yy]=YYY ...)
area3=([17]=seventeen [24]=twenty-four)
echo -n "area3[17] = "
echo ${area3[17]}
echo -n "area3[24] = "
echo ${area3[24]}
# -----------------------------------------------
exit 0
我们可以看出,初始化整数的一个简单的方法是 array=( element1 element2 ... elementN ) 。
base64_charset=( {A..Z} {a..z} {0..9} + / = )
# 使用扩展的一对范围 Using extended brace expansion
#+ 去初始化数组的元素。to initialize the elements of the array.
# 从 vladz 的 "base64.sh" 脚本中摘录过来。
#+ 在"Contributed Scripts" 附录中可以看到.
Bash 允许把变量当成数据来操作,即使这个变量没有明确地被声明为数组。
string=abcABC123ABCabc
echo ${string[@]} # abcABC123ABCabc
echo ${string[*]} # abcABC123ABCabc
echo ${string[0]} # abcABC123ABCabc
echo ${string[1]} # 没有输出!
# 为什么?
echo ${#string[@]} # 1
# 数组中只有一个元素。
# 就是这个字符串本身。
# 感谢你, Michael Zick, 指出这一点.
类似的示范可以参考 Bash变量是无类型的
例子 27-2. 格式化一首诗
#!/bin/bash
# poem.sh: 将本书作者非常喜欢的一首诗,漂亮的打印出来。
# 诗的行数 (单节).
Line[1]="I do not know which to prefer,"
Line[2]="The beauty of inflections"
Line[3]="Or the beauty of innuendoes,"
Line[4]="The blackbird whistling"
Line[5]="Or just after."
# 注意 引用允许嵌入的空格。
# 出处.
Attrib[1]=" Wallace Stevens"
Attrib[2]="\"Thirteen Ways of Looking at a Blackbird\""
# 这首诗已经是公共版权了 (版权已经过期了).
echo
tput bold # 粗体打印.
for index in 1 2 3 4 5 # 5行.
do
printf " %s\n" "${Line[index]}"
done
for index in 1 2 # 出处为2行。
do
printf " %s\n" "${Attrib[index]}"
done
tput sgr0 # 重置终端。Reset terminal.
# 查看 'tput' 文档.
echo
exit 0
# 练习:
# --------
# 修改这个脚本,使其能够从一个文本数据文件中提取出一首诗的内容,然后将其漂亮的打印出来。
数组元素有它们独特的语法,甚至标准Bash命令和操作符,都有特殊的选项用以配合数组操作。
例子 27-3. 多种数组操作
#!/bin/bash
# array-ops.sh: 更多有趣的数组用法.
array=( zero one two three four five )
# 数组元素 0 1 2 3 4 5
echo ${array[0]} # 0
echo ${array:0} # 0
# 第一个元素的参数扩展,
#+ 从位置0(#0)开始(即第一个字符).
echo ${array:1} # ero
# 第一个元素的参数扩扎,
#+ 从位置1(#1)开始(即第二个字符)。
echo "--------------"
echo ${#array[0]} # 4
# 第一个数组元素的长度。
echo ${#array} #4
# 第一个数组元素的长度。
# (另一种表示形式)
echo ${#array[1]} # 3
# 第二个数组元素的长度。
# Bash中的数组是从0开始索引的。
echo ${#array[*]} # 6
# 数组中的元素个数。
echo ${#array[@]} # 6
# 数组中的元素个数.
echo "--------------"
array2=( [0]="first element" [1]="second element" [3]="fourth element" )
# ^ ^ ^ ^ ^ ^ ^ ^ ^
# 引用允许嵌入的空格,在每个单独的数组元素中。
echo ${array2[0]} # 第一个元素
echo ${array2[1]} # 第二个元素
echo ${array2[2]} #
# 因为并没有被初始化,所以此值为null。
echo ${array2[3]} # 第四个元素.
echo ${#array2[0]} # 13 (第一个元素的长度)
echo ${#array2[*]} # 3 (数组中元素的个数)
exit
大部分标准字符串操作 都可以用于数组中。
例子27-4. 用于数组的字符串操作
#!/bin/bash
# array-strops.sh: 用于数组的字符串操作。
# 本脚本由Michael Zick 所编写.
# 通过了授权在本书中使用。
# 修复: 05 May 08, 04 Aug 08.
# 一般来说,任何类似于 ${name ... }(这种形式)的字符串操作
#+ 都能够应用于数组中的所有字符串元素,
#+ 比如说${name[@] ... } 或者 ${name[*] ...} 这两种形式。
arrayZ=( one two three four five five )
echo
# 提取尾部的子串。
echo ${arrayZ[@]:0} # one two three four five five
# ^ 所有元素
echo ${arrayZ[@]:1} # two three four five five
# ^ element[0]后边的所有元素.
echo ${arrayZ[@]:1:2} # two three
# ^ 只提取element[0]后边的两个元素.
echo "---------"
# 子串删除
# 从字符串的开头删除最短的匹配。
echo ${arrayZ[@]#f*r} # one two three five five
# ^ # 匹配将应用于数组的所有元素。
# 匹配到了"four",并且将它删除。
# 从字符串的开头删除最长的匹配
echo ${arrayZ[@]##t*e} # one two four five five
# ^^ # 匹配将应用于数组的所有元素
# 匹配到了 "three" ,并且将它删除。
# 从字符串的结尾删除最短的匹配
echo ${arrayZ[@]%h*e} # one two t four five five
# ^ # 匹配将应用于数组的所有元素
# 匹配到了 "hree" ,并且将它删除。
# 从字符串的结尾删除最长的匹配
echo ${arrayZ[@]%%t*e} # one two four five five
# ^^ # 匹配将应用于数组的所有元素
# 匹配到了 "three" ,并且将它删除。
echo "----------------------"
# 子串替换
# 第一个匹配到的子串将会被替换。
echo ${arrayZ[@]/fiv/XYZ} # one two three four XYZe XYZe
# ^ # 匹配将应用于数组的所有元素
# 所有匹配到的子串将会被替换。
echo ${arrayZ[@]//iv/YY} # one two three four fYYe fYYe
# 匹配将应用于数组的所有元素
# 删除所有的匹配子串
# 如果没有指定替换字符串的话,那就意味着'删除'...
echo ${arrayZ[@]//fi/} # one two three four ve ve
# ^^ # 匹配将应用于数组的所有元素
# 替换字符串前端子串
echo ${arrayZ[@]/#fi/XY} # one two three four XYve XYve
# ^ # 匹配将应用于数组的所有元素
# 替换字符串后端子串
echo ${arrayZ[@]/%ve/ZZ} # one two three four fiZZ fiZZ
# ^ # 匹配将应用于数组的所有元素
echo ${arrayZ[@]/%o/XX} # one twXX three four five five
# ^ # 为什么?
echo "-----------------------------"
replacement() {
echo -n "!!!"
}
echo ${arrayZ[@]/%e/$(replacement)}
# ^ ^^^^^^^^^^^^^^
# on!!! two thre!!! four fiv!!! fiv!!!
# replacement()的标准输出就是那个替代字符串.
# Q.E.D: 替换动作实际上是一个‘赋值’。
echo "------------------------------------"
# 使用"for-each"之前:
echo ${arrayZ[@]//*/$(replacement optional_arguments)}
# ^^ ^^^^^^^^^^^^^
# !!! !!! !!! !!! !!! !!!
# 现在,如果Bash只将匹配到的字符串
#+ 传递给被调用的函数...
echo
exit 0
# 在将处理后的结果发送到大工具之前,比如-- Perl, Python, 或者其它工具
# 回忆一下:
# $( ... ) 是命令替换。
# 一个函数作为子进程运行。
# 一个函数将结果输出到stdout。
# 赋值,结合"echo"和命令替换,
#+ 可以读取函数的stdout.
# 使用name[@]表示法指定了一个 "for-each"
#+ 操作。
# Bash比你想象的更加强力.
命令替换 可以构造数组的独立元素。
例子 27-5. 将脚本中的内容赋值给数组
#!/bin/bash
# script-array.sh: 将脚本中的内容赋值给数组。
# 这个脚本的灵感来自于 Chris Martii 的邮件 (感谢!).
script_contents=( $(cat "$0") ) # 将这个脚本的内容($0)
#+ 赋值给数组
for element in $(seq 0 $((${#script_contents[@]} - 1)))
do # ${#script_contents[@]}
#+ 表示数组元素的个数
#
# 问题:
# 为什么必须使用seq 0 ?
# 用seq 1来试一下.
echo -n "${script_contents[$element]}"
# 在同一行上显示脚本中每个域的内容。
# echo -n "${script_contents[element]}" also works because of ${ ... }.
echo -n " -- " # 使用 " -- " 作为域分隔符。
done
echo
exit 0
# 练习:
# --------
# 修改这个脚本,
#+ 让这个脚本能够按照它原本的格式输出,
#+ 连同空格,换行,等等。
在数组环境中,某些Bash 内建命令 的含义可能会有些轻微的改变。比如,unset 命令可以删除数组元素,甚至能够删除整个数组。
例子 27-6. 一些数组的专有特性
#!/bin/bash
declare -a colors
# 脚本中所有的后续命令都会把
#+ "colors" 当做数组
echo "Enter your favorite colors (separated from each other by a space)."
read -a colors # 至少需要键入3种颜色,以便于后边的演示。
# 'read'命令的特殊选项 ,
#+ 允许给数组元素赋值。
echo
element_count=${#colors[@]}
# 提取数组元素个数的特殊语法
# 用element_count=${#colors[*]} 也可以。
#
# "@" 变量允许在引用中存在单次分割,
#+ (依靠空白字符来分割变量).
#
# 这就好像"$@" 和 "$*"
#+ 在位置参数中所表现出来的行为一样。
index=0
while [ "$index" -lt "$element_count" ]
do # 列出数组中的所有元素
echo ${colors[$index]}
# ${colors[index]} 也可以工作,因为它${ ... }之中.
let "index = $index + 1"
# Or:
# ((index++))
done
# 每个数组元素被列为单独的一行
# 如果没有这种要求的话,可以使用echo -n "${colors[$index]} "
#
# 也可以使用“for”循环来做:
# for i in "${colors[@]}"
# do
# echo "$i"
# done
# (Thanks, S.C.)
echo
# 再次列出数组中的所有元素,不过这次的做法更为优雅。
echo ${colors[@]} # echo ${colors[*]} 也可以工作.
echo
# "unset"命令既可以删除数组数据,也可以删除整个数组。
unset colors[1] # 删除数组的第2个元素。
# 作用等同于colors[1]=
echo ${colors[@]} # 再次列出数组内容,第2个元素没了。
unset colors # 删除整个数组。
# unset colors[*] 以及
#+ unset colors[@] 都可以.
echo; echo -n "Colors gone."
echo ${colors[@]} # 再次列出数组内容,内容为空。
exit 0
正如我们在前面的例子中所看到的,${array_name[@]} 或者 ${array_name[*]} 都与数组中的所有元素相关。同样的,为了计算数组的元素个数,可以使用 ${array_name[@]} 或者 ${array_name[*]}${#array_name} 是数组第一个元素的长度,也就是 ${array_name[0]} 的长度(字符个数)。
例子 27-7. 空数组与包含空元素的数组
#!/bin/bash
# empty-array.sh
# 感谢Stephane Chazelas制作这个例子的原始版本。
#+ 同时感谢Michael Zick 和 Omair Eshkenazi 对这个例子所作的扩展。
# 以及感谢Nathan Coulter 作的声明和感谢。
# 空数组与包含有空元素的数组,这两个概念不同。
array0=( first second third )
array1=( '' ) # "array1" 包含一个空元素.
array2=( ) # 没有元素. . . "array2"为空
array3=() # 这个数组呢?
echo
ListArray()
{
echo
echo "Elements in array0: ${array0[@]}"
echo "Elements in array1: ${array1[@]}"
echo "Elements in array2: ${array2[@]}"
echo "Elements in array3: ${array3[@]}"
echo
echo "Length of first element in array0 = ${#array0}"
echo "Length of first element in array1 = ${#array1}"
echo "Length of first element in array2 = ${#array2}"
echo "Length of first element in array3 = ${#array3}"
echo
echo "Number of elements in array0 = ${#array0[*]}" # 3
echo "Number of elements in array1 = ${#array1[*]}" # 1 (Surprise!)
echo "Number of elements in array2 = ${#array2[*]}" # 0
echo "Number of elements in array3 = ${#array3[*]}" # 0
}
# ===================================================================
ListArray
# 尝试扩展这些数组。
# 添加一个元素到这个数组。
array0=( "${array0[@]}" "new1" )
array1=( "${array1[@]}" "new1" )
array2=( "${array2[@]}" "new1" )
array3=( "${array3[@]}" "new1" )
ListArray
# 或者
array0[${#array0[*]}]="new2"
array1[${#array1[*]}]="new2"
array2[${#array2[*]}]="new2"
array3[${#array3[*]}]="new2"
ListArray
# 如果你按照上边的方法对数组进行扩展的话,数组比较像‘栈’
# 上边的操作就是‘压栈’
# ‘栈’的高度为:
height=${#array2[@]}
echo
echo "Stack height for array2 = $height"
# '出栈’就是:
unset array2[${#array2[@]}-1] # 数组从0开始索引
height=${#array2[@]} #+ 这就意味着数组的第一个下标是0
echo
echo "POP"
echo "New stack height for array2 = $height"
ListArray
# 只列出数组array0的第二个和第三个元素。
from=1 # 从0开始索引。
to=2
array3=( ${array0[@]:1:2} )
echo
echo "Elements in array3: ${array3[@]}"
# 处理方式就像是字符串(字符数组)。
# 试试其他的“字符串”形式。
# 替换:
array4=( ${array0[@]/second/2nd} )
echo
echo "Elements in array4: ${array4[@]}"
# 替换掉所有匹配通配符的字符串
array5=( ${array0[@]//new?/old} )
echo
echo "Elements in array5: ${array5[@]}"
# 当你觉得对此有把握的时候...
array6=( ${array0[@]#*new} )
echo # This one might surprise you.
echo "Elements in array6: ${array6[@]}"
array7=( ${array0[@]#new1} )
echo # 数组array6之后就没有惊奇了。
echo "Elements in array7: ${array7[@]}"
# 看起来非常像...
array8=( ${array0[@]/new1/} )
echo
echo "Elements in array8: ${array8[@]}"
# 所以,让我们怎么形容呢?
# 对数组var[@]中的每个元素The string operations are performed on
#+ 进行连续的字符串操作。each of the elements in var[@] in succession.
# 因此:Bash支持支持字符串向量操作,
# 如果结果是长度为0的字符串
#+ 元素会在结果赋值中消失不见。
# 然而,如果扩展在引用中,那个空元素会仍然存在。
# Michael Zick: 问题--这些字符串是强引用还是弱引用?
# Nathan Coulter: 没有像弱引用的东西
#! 真正发生的事情是
#!+ 匹配的格式发生在
#!+ [word]的所有其它扩展之后
#!+ 比如像${parameter#word}.
zap='new*'
array9=( ${array0[@]/$zap/} )
echo
echo "Number of elements in array9: ${#array9[@]}"
array9=( "${array0[@]/$zap/}" )
echo "Elements in array9: ${array9[@]}"
# 此时,空元素仍然存在
echo "Number of elements in array9: ${#array9[@]}"
# 当你还在认为你身在Kansas州时...
array10=( ${array0[@]#$zap} )
echo
echo "Elements in array10: ${array10[@]}"
# 但是,如果被引用的话,*号将不会被解释。
array10=( ${array0[@]#"$zap"} )
echo
echo "Elements in array10: ${array10[@]}"
# 可能,我们仍然在Kansas...
# (上面的代码块Nathan Coulter所修改.)
# 比较 array7 和array10.
# 比较array8 和array9.
# 重申: 所有所谓弱引用的东西
# Nathan Coulter 这样解释:
# word在${parameter#word}中的匹配格式在
#+ 参数扩展之后和引用移除之前已经完成了。
# 在通常情况下,格式匹配在引用移除之后完成。
exit
${array_name[@]}${array_name[*]} 的关系非常类似于 $@ 和$*。这种数组用法非常广泛。
# 复制一个数组
array2=( "${array1[@]}" )
# 或者
array2="${array1[@]}"
#
# 然而,如果在“缺项”数组中使用的话,将会失败
#+ 也就是说数组中存在空洞(中间的某个元素没赋值),
#+ 这个问题由Jochen DeSmet 指出.
# ------------------------------------------
array1[0]=0
# array1[1] not assigned
array1[2]=2
array2=( "${array1[@]}" ) # 拷贝它?
echo ${array2[0]} # 0
echo ${array2[2]} # (null), 应该是 2
# ------------------------------------------
# 添加一个元素到数组。
array=( "${array[@]}" "new element" )
# 或者
array[${#array[*]}]="new element"
# 感谢, S.C.
info
array=( element1 element2 ... elementN ) 初始化操作,如果有命令替换的帮助,就可以将一个文本文件的内容加载到数组。
#!/bin/bash
filename=sample_file
# cat sample_file
#
# 1 a b c
# 2 d e fg
declare -a array1
array1=( `cat "$filename"`) # 将$filename的内容
# 把文件内容展示到输出 #+ 加载到数组array1.
#
# array1=( `cat "$filename" | tr '\n' ' '`)
# 把文件中的换行替换为空格
# 其实这样做是没必要的,因为Bash在做单词分割的时候,
#+将会把换行转换为空格。
echo ${array1[@]} # 打印数组
# 1 a b c 2 d e fg
#
# 文件中每个被空白符分割的“单词”
#+ 都被保存到数组的一个元素中。
element_count=${#array1[*]}
echo $element_count # 8
出色的技巧使得数组的操作技术又多了一种。
例子 27-8. 初始化数组
#! /bin/bash
# array-assign.bash
# 数组操作是Bash所特有的,
#+ 所以才使用".bash" 作为脚本扩展名
# Copyright (c) Michael S. Zick, 2003, All rights reserved.
# License: Unrestricted reuse in any form, for any purpose.
# Version: $ID$
#
# 说明与注释由 William Park所添加.
# 基于 Stephane Chazelas所提供的例子
#+ 它是在ABS中的较早版本。
# 'times' 命令的输出格式:
# User CPU <space> System CPU
# User CPU of dead children <space> System CPU of dead children
# Bash有两种方法,
#+ 可以将一个数组的所有元素都赋值给一个新的数组变量。
# 这两个方法都会丢弃数组中的“空引用“(null值)元素
#+ 在2.04和以后的Bash版本中。
# 另一种给数组赋值的方法将会被添加到新版本的Bash中,
#+ 这种方法采用[subscript]=value 形式,来维护数组下标与元素值之间的关系。
# 可以使用内部命令来构造一个大数组,
#+ 当然,构造一个包含上千元素数组的其它方法
#+ 也能很好的完成任务
declare -a bigOne=( /dev/* ) # /dev下的所有文件 . . .
echo
echo 'Conditions: Unquoted, default IFS, All-Elements-Of'
echo "Number of elements in array is ${#bigOne[@]}"
# set -vx
echo
echo '- - testing: =( ${array[@]} ) - -'
times
declare -a bigTwo=( ${bigOne[@]} )
# 注意括号: ^ ^
times
echo
echo '- - testing: =${array[@]} - -'
times
declare -a bigThree=${bigOne[@]}
# 这次没用括号。
times
# 通过比较,可以发现第二种格式的赋值更快一些,
#+ 正如 Stephane Chazelas指出的那样
#
# William Park 解释:
#+ bigTwo数组是作为一个单个字符串被赋值的(因为括号)
#+ 而BigThree数组,则是一个元素一个元素进行的赋值。
# 所以,实质上是:
# bigTwo=( [0]="..." [1]="..." [2]="..." ... )
# bigThree=( [0]="... ... ..." )
#
# 通过这样确认: echo ${bigTwo[0]}
# echo ${bigThree[0]}
# 在本书的例子中,我还是会继续使用第一种形式,
#+ 因为,我认为这种形式更有利于将问题阐述清楚。
# 在我所使用的例子中,在其中复用的部分,
#+ 还是使用了第二种形式,那是因为这种形式更快。
# MSZ: 很抱歉早先的疏忽。
# 注意:
# ----
# 32和44的"declare -a" 语句其实不是必需的,
#+ 因为Array=(...)形式
#+ 只能用于数组
# 然而,如果省略这些声明的话,
#+ 会导致脚本后边的相关操作变慢。
# 试试看,会发生什么.
exit 0
extra
在数组声明的时候添加一个额外的declare -a语句,能够加速后续的数组操作速度。
例子 27-9. 拷贝和连接数组
#! /bin/bash
# CopyArray.sh
#
# 这个脚本由Michael Zick所编写.
# 这里已经通过作者的授权
# 如何“通过名字传值&通过名字返回”
#+ 或者“建立自己的赋值语句”。
CpArray_Mac() {
# 建立赋值命令
echo -n 'eval '
echo -n "$2" # 目的名字
echo -n '=( ${'
echo -n "$1" # 源名字
echo -n '[@]} )'
# 上边这些语句会构成一条命令。
# 这仅仅是形式上的问题。
}
declare -f CopyArray
CopyArray=CpArray_Mac
Hype() {
# "Pointer"函数
# 状态产生器
# 需要连接的数组名为$1.
# (把这个数组与字符串"Really Rocks"结合起来,形成一个新数组.)
# 并将结果从数组$2中返回.
local -a TMP
local -a hype=( Really Rocks )
$($CopyArray $1 TMP)
TMP=( ${TMP[@]} ${hype[@]} )
$($CopyArray TMP $2)
}
declare -a before=( Advanced Bash Scripting )
declare -a after
echo "Array Before = ${before[@]}"
Hype before after
echo "Array After = ${after[@]}"
# 连接的太多了?
echo "What ${after[@]:3:2}?"
declare -a modest=( ${after[@]:2:1} ${after[@]:3:2} )
# ---- 子串提取 ----
echo "Array Modest = ${modest[@]}"
# 'before' 发生了什么变化 ?
echo "Array Before = ${before[@]}"
exit 0
例子27-10. 关于串联数组的更多信息
#! /bin/bash
# array-append.bash
# Copyright (c) Michael S. Zick, 2003, All rights reserved.
# License: Unrestricted reuse in any form, for any purpose.
# Version: $ID$
#
# 在格式上,由M.C做了一些修改.
# 数组操作是Bash特有的属性。
# 传统的UNIX /bin/sh 缺乏类似的功能。
# 将这个脚本的输出通过管道传递给'more',
#+ 这样做的目的是放止输出的内容超过终端能够显示的范围,
# 或者,重定向输出到文件中。
declare -a array1=( zero1 one1 two1 )
# 依次使用下标
declare -a array2=( [0]=zero2 [2]=two2 [3]=three2 )
# 数组中存在空缺的元素-- [1] 未定义
echo
echo '- Confirm that the array is really subscript sparse. -'
echo "Number of elements: 4" # 为了演示,这里作了硬编码
for (( i = 0 ; i < 4 ; i++ ))
do
echo "Element [$i]: ${array2[$i]}"
done
# 也可以参考一个更通用的例子, basics-reviewed.bash.
declare -a dest
# 将两个数组合并到第3个数组中。
echo
echo 'Conditions: Unquoted, default IFS, All-Elements-Of operator'
echo '- Undefined elements not present, subscripts not maintained. -'
# # 那些未定义的元素不会出现;组合时会丢弃这些元素。
dest=( ${array1[@]} ${array2[@]} )
# dest=${array1[@]}${array2[@]} # 奇怪的结果,可能是个bug。
# 现在,打印结果。
echo
echo '- - Testing Array Append - -'
cnt=${#dest[@]}
echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
echo "Element [$i]: ${dest[$i]}"
done
# 将数组赋值给一个数组中的元素(两次)
dest[0]=${array1[@]}
dest[1]=${array2[@]}
# 打印结果
echo
echo '- - Testing modified array - -'
cnt=${#dest[@]}
echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
echo "Element [$i]: ${dest[$i]}"
done
# 检查第二个元素的修改状况.
echo
echo '- - Reassign and list second element - -'
declare -a subArray=${dest[1]}
cnt=${#subArray[@]}
echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
echo "Element [$i]: ${subArray[$i]}"
done
# 如果你使用'=${ ... }'形式
#+ 将一个数组赋值到另一个数组的一个元素中,
#+ 那么这个数组的所有元素都会被转换为一个字符串,
#+ 这个字符串中的每个数组元素都以空格进行分隔(其实是IFS的第一个字符).
# 如果原来数组中的所有元素都不包含空白符 . . .
# 如果原来的数组下标都是连续的 . . .
# 那么我们就可以将原来的数组进行恢复.
# 从修改过的第二个元素中, 将原来的数组恢复出来.
echo
echo '- - Listing restored element - -'
declare -a subArray=( ${dest[1]} )
cnt=${#subArray[@]}
echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
echo "Element [$i]: ${subArray[$i]}"
done
echo '- - Do not depend on this behavior. - -'
echo '- - This behavior is subject to change - -'
echo '- - in versions of Bash newer than version 2.05b - -'
# MSZ: 抱歉,之前混淆了一些要点。
exit 0

有了数组, 我们就可以在脚本中实现一些比较熟悉的算法. 这么做, 到底是不是一个好主意, 我们在这里不做讨论, 还是留给读者决定吧.
例子 27-11. 冒泡排序
#!/bin/bash
# bubble.sh: 一种排序方式, 冒泡排序.
# 回忆一下冒泡排序的算法. 我们在这里要实现它...
# 依靠连续的比较数组元素进行排序,
#+ 比较两个相邻元素, 如果顺序不对, 就交换这两个元素的位置.
# 当第一轮比较结束之后, 最"重"的元素就会被移动到最底部.
# 当第二轮比较结束之后, 第二"重"的元素就会被移动到次底部的位置.
# 依此类推.
# 这意味着每轮比较不需要比较之前已经"沉淀"好的数据.
# 因此你会注意到后边的数据在打印的时候会快一些.
exchange() {
# 交换数组中的两个元素.
local temp=${Countries[$1]} # 临时保存
#+ 要交换的那个元素
Countries[$1]=${Countries[$2]}
Countries[$2]=$temp
return