高级shell编程笔记(第二十六章 数组)

第二十六章 数组

较新的Bash版本支持一维数组。数组元素可以用符号variable[xx]来初始化。另外,脚本可以用declare -a variable 语句来清楚地指定一个数组。要访问一个数组元素,可以使用花括号来访问,即${variable[xx]}。

Example 26-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"
area2=(zero one two three four)
echo -n "area2[0] = "
echo ${area2[0]}

echo -n "area2[1] = "
echo ${area2[1]}
echo;echo;echo

# -----------------------------------------------
#第三种数组,"area2"
area3=([17]=seventeen [24]=twenty-four)
echo -n "area3[17] = "
echo ${area3[17]}

echo -n "area3[24] = "
echo ${area3[24]}

exit 0

注意:Bash允许把变量当成数组来操作,即使这个变量没有明确的被声明为数组

string=abcABC123ABCabc
echo ${string[@]}
echo ${string[*]}
echo ${string[0]}
echo ${string[1]}   #无输出,因为数组中只有一个元素,且只是字符串本身
echo ${#string[@]}

Example 26-2 格式化一首诗

#!/bin/bash
#
#诗的行数
Line[1]="天门中断楚江开,"
Line[2]="碧水东流至此回。"
Line[3]="两岸青山相对出,"
Line[4]="孤帆一片日边来。"

#出处
Author[1]="《望天门山》"
Author[2]="     --李白"
Author[3]="     --唐代"

echo
for index in 1 2 3
do
  printf "       %s\n" "${Author[index]}"
done

for index in 1 2 3 4
do
  printf "     %s\n" "${Line[index]}"
done
echo
exit 0

数组元素有它们独有的语法,并且甚至Bash命令和操作符有特殊的选项可以支持数组使用

Example 26-3 多数组操作

#!/bin/bash
#
array=(zero one two three four five)
echo ${array[0]}   #zero
echo ${array:0}   #zero
#对数组的第一个元素"zero"进行字符串截取0位(从左到右)

echo ${array:1}   #ero
#对数组的第一个元素"zero"进行字符串截取1位(从左到右)

echo "-----------------------"
echo ${#array[0]}   #4
#数组第一个元素"zero"的长度
echo ${#array}   #4
#数组第一个元素"zero"的长度(另一种写法)

echo ${#array[1]}   #3
#数组第二个元素"one"的长度

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]}   # 第四个元素

exit 0

大部分标准的字符串操作符可以用于数组操作

Example 26-4 用于数组的字符串操作符

#!/bin/bash
#
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
echo ${arrayZ[@]:1:2}   #two three
echo "----------------------"

echo ${arrayZ[@]#f*r}   #one two three five five
echo ${arrayZ[@]##t*e}   #one two four five five
echo ${arrayZ[@]%h*e}   #one two t four five five
echo ${arrayZ[@]%%t*e}   #one two four five five
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
#为什么"one"没有替换为"XXne"?
echo "----------------------"

newstr(){
    echo -n "!!!"
}

echo ${arrayZ[@]/%e/$(newstr)}   #on!!! two thre!!! four fiv!!! fiv!!!
echo ${arrayZ[@]//*/$(newstr optional_arguments)}   #!!! !!! !!! !!! !!! !!!
echo
exit 0

命令替换能创建数组的新的单个元素

Example 26-5 将脚本的内容传进数组

#!/bin/bash
#
script_contents=($(cat "$0"))

for element in $(seq 0 $((${#script_contents[@]} - 1)))
do
  echo -n "${script_contents[$element]}"
  echo -n "--"
done
echo
exit 0

在数组的环境里,一些Bash内建命令含义有一些轻微的改变。例如:unset会删除数组元素,或者甚至删除整个数组

Example 26-6 一些数组专用的工具

#!/bin/bash
#
declare -a colors
#所有脚本后面的命令都会把变量"colors"作为数组对待

echo "输入您最喜欢的颜色(用空格隔开)."
read -a colors   #键入至少 3 种颜色以用于下面的示例.

echo
element_count=${#colors[@]}

index=0
while [ "$index" -lt "$element_count" ]
do
  echo ${colors[$index]}
  let "index = $index + 1"
done
echo

#再次列出数组中所有的元素
echo ${colors[@]}
echo

#"unset"命令删除一个数组元素或是整个数组.
unset colors[1]   #删除数组的第二个元素  :作用等同于colors[1]=
echo ${colors[@]}

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]}的长度(字符数)。

Example 26-7 关于数组和空数组元素

#!/bin/bash
#
array0=(first second third)
array1=("")
array2=()

echo
ListArray()
{
    echo
    echo "Elements in array0: ${array0[@]}"   #first second third
    echo "Elements in array1: ${array1[@]}"   #空
    echo "Elements in array2: ${array2[@]}"   #空
    echo
    echo "Length of first element in array0 = ${#array0}"   #5
    echo "Length of first element in array1 = ${#array1}"   #0
    echo "Length of first element in array2 = ${#array2}"   #0
    echo
    echo "Number of elements in array0 = ${#array0[*]}"   #3
    echo "Number of elements in array1 = ${#array1[*]}"   #1
    echo "Number of elements in array2 = ${#array2[*]}"   #0
}
ListArray

#增加一个元素到数组
array0=("${array0[@]}" "new1")
array1=("${array1[@]}" "new1")
array2=("${array2[@]}" "new1")

ListArray

#或
array0[${#array0[*]}]="new2"
array1[${#array1[*]}]="new2"
array2[${#array2[*]}]="new2"

ListArray
#当像上面的做法增加数组时,数组像"栈",上面的做法是"push(压栈)"
#栈高是:
height=${#array2[@]}
echo
echo "array2的堆栈高度 = $height"

#"pop(出栈)"是:
unset array2[${#array2[@]}-1]
helght=${#array2[@]}
echo 
echo "POP"
echo "array2的新堆栈高度 = $helght"

ListArray

#只列出数组array0的第二和第三个元素
from=1
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
echo "Elements in array6: ${array6[@]}"

array7=(${array0[@]#new1})
echo
echo "Elements in array7: ${array7[@]}"
#这看起来非常像
array8=(${array0[@]/new1/})
echo
echo "Elements in attay8: ${array8[@]}"

zap='new*'
array9=(${array0[@]/$zap/})
echo
echo "Elements in array9: ${array9[@]}"

array10=(${array0[@]#$azp})
echo
echo "Elements in array10: ${array10[@]}"
#把 array7 和 array10 比较.
# 把 array8 和 array9 比较.
#答案: 必须用弱引用.
exit 0

${array_name[@]}和${array_name[*]}的关系类似于 “$@” 和 “$*” 。这种数组用法非常有用

#复制一个数组
array2=("${array1[@]}")
#或
array2="${array1[@]}"

#给数组增加一个元素
array=("${array[@]}" "new element")
#或
array[${#array[*]}]="new element"

注意:array=(element1 element2 … elementN)初始化操作依赖于命令替换(command substitution)使将一个文本内容加载进数组成为可能。

#!/bin/bash
#
filename=sample_file
#cat sample_file
#1 a b c
#2 d e fg
declare -a array1
array1=(`cat "$filename"`)
#array1=(`cat "$filename" | tr '\n' ''`)
#把文件里的换行变为空格.  :这是没必要的,因为 Bash 做单词分割时会把换行变为空格.
echo ${array1[@]}   #1 a b c 2 d e fg

element_count=${#array1[*]}
echo $element_count   #8

Example 26-8 初始化数组

#!/bin/bash
#array-assign.bash
#数组操作是Bash特有的,因此脚本名用".bash"结尾。
declare -a bigOne=(/dev/*)
echo
echo "条件:未加引号、默认IFS、所有元素"
echo "One数组中的元素数为 ${#bigOne[@]}"   #153
echo
echo '- - testing: =(${array[@]}) - -'
times
declare -a bigTwo=(${bigOne[@]})
echo "Two数组中的元素数为 ${#bigTwo[@]}"   #153
times
echo
echo '- - testing: =${array[@]}'
times
declare -a bigThree=${bigOne[@]}   #这次没有括号
echo "Three数组中的元素数为 ${#bigThree[@]}"   #1
times
#第二种格式的赋值比第三和第四的 times 的更快
exit 0

注意:对变量增加"declare -a"语句声明可以加速后面的数组操作速度

Example 26-9 复制和连接数组(有点绕,勉强看懂)

#!/bin/bash
#
CpArray_Mac()
{
    echo -n 'eval '
    echo -n "$2"   #目的变量名
    echo -n '=(${'
    echo -n "$1"   #源名字
    echo -n '[@]})'
}

declare -f CopyArray
CopyArray=CpArray_Mac

Hype()
{
    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[@]}"

echo "Array Before = ${before[@]}"

exit 0

Example 26-10 关于连接数组的更多信息

#!/bin/bash
#
declare -a array1=(zero1 one1 two1)
declare -a array2=([0]=zero2 [2]=two2 [3]=three2)
echo

echo "确认数组确实是下标稀疏的"
echo "Number of elements: 4"
for ((i=0;i<4;i++))
do
  echo "Element [$i]: ${array2[$i]}"
done

declare -a dest
echo
echo "条件:无引号,默认IFS,运算符的所有元素"
echo "不存在未定义的元素,未维护下标"
dest=(${array1[@]} ${array2[@]})
echo
echo "- - Testing Array Append - -"
cnt=${#dest[@]}

echo "Number of elements: $cnt"   #6
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

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 "不要依赖这种行为"
echo "这种行为可能会改变"
echo "在比2.05b版本更新的Bash版本中"
exit 0

数组允许在脚本中实现一些常见的熟悉算法。

Example 26-11 一位老朋友:冒泡排序

#!/bin/bash
#
exchange()
{
    #交换数组的两个元素
    local temp=${Countries[$1]}   #临时保存要交换的一个元素
    Countries[$1]=${Countries[$2]}
    Countries[$2]=$temp
    return
}

declare -a Countries
Countries=(Netherlands Ukraine Zaire Turkey Russia Yemen Syria \
Xanadu Qatar Liechtenstein Hungary)

clear

echo "0:${Countries[*]}"   #从0索引的元素开始列出整个数组

number_of_elements=${#Countries[@]}
let "comparisons = $number_of_elements - 1"
count=1
while [ "$comparisons" -gt 0 ]
do
  index=0
  while [ "$index" -lt "$comparisons" ]
  do
    if [ ${Countries[$index]} \> ${Countries[`expr $index + 1`]} ];then
    # "\>" 在单方括号里是is ASCII码的比较操作符
    # if [[ ${Countries[$index]} > ${Countries[`expr $index + 1`]} ]]  也可以 
      exchange $index `expr $index + 1`
    fi
    let "index += 1"
  done
  let "comparisons -= 1"
  echo
  echo "$count: ${Countries[@]}"
  echo
  let "count += 1"
done
exit 0

在数组内嵌一个数组有可能做到吗?

#!/bin/bash
#
AnArray=($(ls --inode --ignore-backups --almost-all \
--directory --full-time --color=none --time=status \
--sort=time -l ${PWD}))

SubArray=(${AnArray[@]:11:1} ${AnArray[@]:6:5})
#这个数组有6个元素:
#SubArray=( [0]=${AnArray[11]} [1]=${AnArray[6]} [2]=${AnArray[7]}
# [3]=${AnArray[8]} [4]=${AnArray[9]} [5]=${AnArray[10]} )

echo "当前目录和上次状态更改的日期:"
echo "${SubArray[@]}"
exit 0

内嵌数组和间接引用的组合使用产生了一些有趣的用法

Example 26-12 内嵌数组和间接引用

#!/bin/bash
#
ARRAY1=(VAR1_1=value11 VAR1_2=value12 VAR1_3=value13)

ARRAY2=(VARIABLE="test" STRING="VAR1=value1 VAR2=value2 VAR3=value3" ARRAY21=${ARRAY1[*]})

function print()
{
    OLD_IFS="$IFS"
    IFS=$'\n'   #这是为了在每行打印一个数组元素
    
    TEST1="ARRAY2[*]"
    local ${!TEST1}   #间接引用
    echo
    echo "\$TEST1 = $TEST1"
    echo;echo
    echo "{\$TEST1} = ${!TEST1}"
    
    echo
    echo "-------------------------------"
    echo
    
    echo "Variable VARIABLE: $VARIABLE"
    IFS="$OLD_IFS"
    TEST2="STRING[*]"
    local ${!TEST2}
    echo "String element VAR2:$VAR2 from STRING"
    TEST2="ARRAY21[@]"
    local ${!TEST2}
    echo "Array element VAR1_1:$VAR1_1 from ARRAY21"
}
print
echo
exit 0

Example 26-13 复杂数组应用:埃拉托色尼素数筛子

#!/bin/bash
#
LOWER_LIMIT=1
UPPER_LIMIT=1000

PRIME=1
NON_PRIME=0

let SPLIT=UPPER_LIMIT/2

declare -a Primes

initialize()
{
    i=$LOWER_LIMIT
    until [ "$i" -gt "$UPPER_LIMIT" ]
    do
      Primes[i]=$PRIME
      let "i += 1"
    done
}

print_primes()
{
    i=$LOWER_LIMIT
    until [ "$i" -gt "$UPPER_LIMIT" ]
    do
      if [ "${Primes[i]}" -eq "$PRIME" ];then
        printf "%8d" $i
      fi
      let "i += 1"
    done
}

sift()
{
    let i=$LOWER_LIMIT+1
    until [ "$i" -gt "$UPPER_LIMIT" ]
    do
      if [ "${Primes[i]}" -eq "$PRIME" ];then
        t=$i
        while [ "$t" -le "$UPPER_LIMIT" ]
        do
          let "t += $i"
          Primes[t]=$NON_PRIME
        done
      fi
      let "i += 1"
    done
}

#==================================
#main ()
#继续调用函数
initialize
sift
print_primes
#这就是被称为结构化编程的东西了
#==================================

echo
exit 0

#
#-----------------------------------
#下面是一个改进版,需要位置参数
UPPER_LIMIT=$1
let SPLIT=UPPER_LIMIT/2

Primes=('' $(seq $UPPER_LIMIT))
i=1
until (( (i += 1) > SPLIT ))
do
  if [[ -n $Primes[i] ]];then
    t=$i
    until (( (t += i) > UPPER_LIMIT ))
    do
      Primes[t]=
    done
  fi
done
echo ${Primes[*]}
exit 0

比较这个用数组的素数产生器和另一种不用数组的例子A-16

数组可以做一定程度的扩展,以模拟支持Bash原本不支持的数据结构

Example 26-14 模拟下推的堆栈

#!/bin/bash
#
BP=100
SP=$BP
Data=
declare -a stack

push()
{
    if [ -z "$1" ];then
      return
    fi
    
    let "SP -= 1"
    stack[$SP]=$1
    
    return
}

pop ()
{
    Data=
    if [ "$SP" -eq "$BP" ];then
      return
    fi
    
    Data=${stack[$SP]}
    let "SP += 1"
    return  
}

status_report()
{
    echo "--------------------------"
    echo "REPORT"
    echo "Stack Pointer = $SP"
    echo "Just popped \""$Data"\" off the stack"
    echo "--------------------------"
    echo;echo;echo;echo
}

echo
pop
status_report
echo

push garbage
pop
status_report

value1=23;push $value1
value2=skidoo;push $value2
value3=FINAL;push $value3

pop
status_report   #FINAL
pop
status_report   #skidoo
pop
status_report   #23
#后进的先出
#注意堆栈指针:每次压栈时减,每次弹出是加。
echo
exit 0

Example 26-15 复杂的数组应用:列出一种怪异的数学序列

#!/bin/bash
#
LIMIT=100
LINEWIDTH=20

Q[1]=1
Q[2]=1

echo
echo "Q-series [$LIMIT terms]:"
echo -n "${Q[1]}"
echo -n "${Q[2]}"

for (( n=3;n<=$LIMIT;n++))
do
  let "n1 = $n - 1"   #n-1
  let "n2 = $n - 2"   #n-2
  t0=`expr $n - ${Q[n1]}`   #n - Q[n-1]
  t1=`expr $n - ${Q[n2]}`   #n - Q[n-2]
  T0=${Q[t0]}   #Q[n - Q[n-1]]
  T1=${Q[t1]}   #Q[n - Q[n-2]]
  
  Q[n]=`expr $T0 + $T1`   #Q[n - Q[n-1]] + Q[n - Q[n-2]]
  echo -n "${Q[n]}"
  
  if [ `expr $n % $LINEWIDTH` -eq 0 ];then
    echo
  fi
done
echo
exit 0

Bash只支持一维数组,但有些技巧可用来模拟多维数组

Example 26-16 模拟二维数组,并使它倾斜(没完全看明白)

#!/bin/bash
#
#一维数组由单行组成.
#二维数组由连续的行组成.

Rows=5
Columns=5

declare -a alpha

load_alpha()
{
    local rc=0
    local index
    for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y
    do
      local row=`expr $rc / $Columns`
      local column=`expr $rc % $Rows`
      let "index = $row * $Rows + $column"
      alpha[$index]=$i
      let "rc += 1"
    done  
}
#更简单的办法
#declare -a alpha=( A B C D E F G H I J K L M N O P Q R S T U V W X Y )
#但这就缺少了二维数组的感觉了

print_alpha ()
{
    local row=0
    local index
    echo
    while [ "$row" -lt "$Rows" ]   #以行顺序为索引打印行的各元素
                                   #:即数组列值变化快,行值变化慢
    do
      local column=0
      echo -n "          "   #依行倾斜打印正方形的数组
      while [ "$column" -lt "$Columns" ]
      do
        let "index = $row *$Rows + $column"
        echo -n "${alpha[index]}"
        let "column += 1"
      done
      let "row += 1"
      echo
    done
    #等同于:echo ${alpha[*]} | xargs -n $Columns
    echo    
}

filter ()   #过滤出负数的数组索引.
{
    echo -n "          "   #产生倾斜角度
    if [[ "$1" -ge 0 && "$1" -lt "$Rows" && "$2" -ge 0 && "$2" -lt "$Columns" ]]
    then
      let "index = $1 * $Rows + $2"
      #现在打印旋转角度.
      echo -n "${alpha[index]}"
    fi  
}

rotate ()   #旋转数组45度   :在左下角"平衡"图形
{
    local row
    local column
    
    for ((row = Rows; row > -Rows; row--))
    do
      for ((column = 0; column < Columns; column++))
      do
        if [ "$row" -ge 0 ];then
          let "t1 = $column - $row"
          let "t2 = $column"
        else
          let "t1 = $column"
          let "t2 = $column + $row"
        fi
        filter $t1 $t2
      done
      echo;echo
    done        
}

load_alpha   #加载数组
print_alpha   #打印数组
rotate   #逆时针旋转数组45度

exit 0

二维数组本质上等同于一维数组,而只增加了使用行和列的位置来引用和操作元素的寻址模式。

关于二维数组更好的例子,请参考例子A-10
另一个有趣的使用数组的脚本:例子14-3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值