📄 arrays.html
字号:
10 # 依此类推. 11 # 这意味着每轮比较不需要比较之前已经"沉淀"好的数据. 12 # 因此你会注意到后边的数据在打印的时候会快一些. 13 14 15 exchange() 16 { 17 # 交换数组中的两个元素. 18 local temp=${Countries[$1]} # 临时保存 19 #+ 要交换的那个元素. 20 Countries[$1]=${Countries[$2]} 21 Countries[$2]=$temp 22 23 return 24 } 25 26 declare -a Countries # 声明数组, 27 #+ 此处是可选的, 因为数组在下面被初始化. 28 29 # 我们是否可以使用转义符(\) 30 #+ 来将数组元素的值放在不同的行上? 31 # 可以. 32 33 Countries=(Netherlands Ukraine Zaire Turkey Russia Yemen Syria \ 34 Brazil Argentina Nicaragua Japan Mexico Venezuela Greece England \ 35 Israel Peru Canada Oman Denmark Wales France Kenya \ 36 Xanadu Qatar Liechtenstein Hungary) 37 38 # "Xanadu"虚拟出来的世外桃源. 39 #+ 40 41 42 clear # 开始之前的清屏动作. 43 44 echo "0: ${Countries[*]}" # 从索引0开始列出整个数组. 45 46 number_of_elements=${#Countries[@]} 47 let "comparisons = $number_of_elements - 1" 48 49 count=1 # 传递数字. 50 51 while [ "$comparisons" -gt 0 ] # 开始外部循环 52 do 53 54 index=0 # 在每轮循环开始之前, 重置索引. 55 56 while [ "$index" -lt "$comparisons" ] # 开始内部循环 57 do 58 if [ ${Countries[$index]} \> ${Countries[`expr $index + 1`]} ] 59 # 如果原来的排序次序不对... 60 # 回想一下, 在单括号中, 61 #+ \>是ASCII码的比较操作符. 62 63 # if [[ ${Countries[$index]} > ${Countries[`expr $index + 1`]} ]] 64 #+ 这样也行. 65 then 66 exchange $index `expr $index + 1` # 交换. 67 fi 68 let "index += 1" # 或者, index+=1 在Bash 3.1之后的版本才能这么用. 69 done # 内部循环结束 70 71 # ---------------------------------------------------------------------- 72 # Paulo Marcel Coelho Aragao建议我们可以使用更简单的for循环. 73 # 74 # for (( last = $number_of_elements - 1 ; last > 1 ; last-- )) 75 # do 76 # for (( i = 0 ; i < last ; i++ )) 77 # do 78 # [[ "${Countries[$i]}" > "${Countries[$((i+1))]}" ]] \ 79 # && exchange $i $((i+1)) 80 # done 81 # done 82 # ---------------------------------------------------------------------- 83 84 85 let "comparisons -= 1" # 因为最"重"的元素到了底部, 86 #+ 所以每轮我们可以少做一次比较. 87 88 echo 89 echo "$count: ${Countries[@]}" # 每轮结束后, 都打印一次数组. 90 echo 91 let "count += 1" # 增加传递计数. 92 93 done # 外部循环结束 94 # 至此, 全部完成. 95 96 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>--</P><P>我们可以在数组中嵌套数组么? </P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # "嵌套"数组. 3 4 # Michael Zick提供了这个用例, 5 #+ William Park做了一些修正和说明. 6 7 AnArray=( $(ls --inode --ignore-backups --almost-all \ 8 --directory --full-time --color=none --time=status \ 9 --sort=time -l ${PWD} ) ) # 命令及选项. 10 11 # 空格是有意义的 . . . 并且不要在上边用引号引用任何东西. 12 13 SubArray=( ${AnArray[@]:11:1} ${AnArray[@]:6:5} ) 14 # 这个数组有六个元素: 15 #+ SubArray=( [0]=${AnArray[11]} [1]=${AnArray[6]} [2]=${AnArray[7]} 16 # [3]=${AnArray[8]} [4]=${AnArray[9]} [5]=${AnArray[10]} ) 17 # 18 # Bash数组是字符串(char *)类型 19 #+ 的(循环)链表. 20 # 因此, 这不是真正意义上的嵌套数组, 21 #+ 只不过功能很相似而已. 22 23 echo "Current directory and date of last status change:" 24 echo "${SubArray[@]}" 25 26 exit 0</PRE></FONT></TD></TR></TABLE></P><P>--</P><P>如果将<SPANCLASS="QUOTE">"嵌套数组"</SPAN>与<AHREF="bashver2.html#VARREFNEW">间接引用</A>组合起来使用的话, 将会产生一些非常有趣的用法. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EMBARR"></A><P><B>例子 26-12. 嵌套数组与间接引用</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # embedded-arrays.sh 3 # 嵌套数组和间接引用. 4 5 # 本脚本由Dennis Leeuw编写. 6 # 经过授权, 在本书中使用. 7 # 本书作者做了少许修改. 8 9 10 ARRAY1=( 11 VAR1_1=value11 12 VAR1_2=value12 13 VAR1_3=value13 14 ) 15 16 ARRAY2=( 17 VARIABLE="test" 18 STRING="VAR1=value1 VAR2=value2 VAR3=value3" 19 ARRAY21=${ARRAY1[*]} 20 ) # 将ARRAY1嵌套到这个数组中. 21 22 function print () { 23 OLD_IFS="$IFS" 24 IFS=$'\n' # 这么做是为了每行 25 #+ 只打印一个数组元素. 26 TEST1="ARRAY2[*]" 27 local ${!TEST1} # 删除这一行, 看看会发生什么? 28 # 间接引用. 29 # 这使得$TEST1 30 #+ 只能够在函数内被访问. 31 32 33 # 让我们看看还能干点什么. 34 echo 35 echo "\$TEST1 = $TEST1" # 仅仅是变量名字. 36 echo; echo 37 echo "{\$TEST1} = ${!TEST1}" # 变量内容. 38 # 这就是 39 #+ 间接引用的作用. 40 echo 41 echo "-------------------------------------------"; echo 42 echo 43 44 45 # 打印变量 46 echo "Variable VARIABLE: $VARIABLE" 47 48 # 打印一个字符串元素 49 IFS="$OLD_IFS" 50 TEST2="STRING[*]" 51 local ${!TEST2} # 间接引用(同上). 52 echo "String element VAR2: $VAR2 from STRING" 53 54 # 打印一个数组元素 55 TEST2="ARRAY21[*]" 56 local ${!TEST2} # 间接引用(同上). 57 echo "Array element VAR1_1: $VAR1_1 from ARRAY21" 58 } 59 60 print 61 echo 62 63 exit 0 64 65 # 脚本作者注, 66 #+ "你可以很容易的将其扩展成一个能创建hash的Bash脚本." 67 # (难) 留给读者的练习: 实现它. </PRE></FONT></TD></TR></TABLE><HR></DIV><P>--</P><P>数组使得<EM>埃拉托色尼素数筛子</EM>有了shell版本的实现. 当然, 如果你需要的是追求效率的应用, 那么就应该使用编译行语言来实现, 比如C语言. 因为脚本运行的太慢了. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX68"></A><P><B>例子 26-13. 复杂的数组应用: <EM>埃拉托色尼素数筛子</EM></B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # sieve.sh (ex68.sh) 3 4 # 埃拉托色尼素数筛子 5 # 找素数的经典算法. 6 7 # 在同等数值的范围内, 8 #+ 这个脚本运行的速度比C版本慢的多. 9 10 LOWER_LIMIT=1 # 从1开始. 11 UPPER_LIMIT=1000 # 到1000. 12 # (如果你时间很多的话 . . . 你可以将这个数值调的很高.) 13 14 PRIME=1 15 NON_PRIME=0 16 17 let SPLIT=UPPER_LIMIT/2 18 # 优化: 19 # 只需要测试中间到最大的值(为什么?). 20 # (译者注: 这个变量在脚本正文并没有被使用, 仅仅在107行之后的优化部分才使用.) 21 22 declare -a Primes 23 # Primes[]是个数组. 24 25 26 initialize () 27 { 28 # 初始化数组. 29 30 i=$LOWER_LIMIT 31 until [ "$i" -gt "$UPPER_LIMIT" ] 32 do 33 Primes[i]=$PRIME 34 let "i += 1" 35 done 36 # 假定所有数组成员都是需要检查的(素数) 37 #+ 直到检查完成. 38 } 39 40 print_primes () 41 { 42 # 打印出所有数组Primes[]中被标记为素数的元素. 43 44 i=$LOWER_LIMIT 45 46 until [ "$i" -gt "$UPPER_LIMIT" ] 47 do 48 49 if [ "${Primes[i]}" -eq "$PRIME" ] 50 then 51 printf "%8d" $i 52 # 每个数字打印前先打印8个空格, 在偶数列才打印. 53 fi 54 55 let "i += 1" 56 57 done 58 59 } 60 61 sift () # 查出非素数. 62 { 63 64 let i=$LOWER_LIMIT+1 65 # 我们都知道1是素数, 所以我们从2开始. 66 # (译者注: 从2开始并不是由于1是素数, 而是因为要去掉以后每个数的倍数, 感谢网友KevinChen.) 67 until [ "$i" -gt "$UPPER_LIMIT" ] 68 do 69 70 if [ "${Primes[i]}" -eq "$PRIME" ] 71 # 不要处理已经过滤过的数字(被标识为非素数). 72 then 73 74 t=$i 75 76 while [ "$t" -le "$UPPER_LIMIT" ] 77 do 78 let "t += $i " 79 Primes[t]=$NON_PRIME 80 # 标识为非素数. 81 done 82 83 fi 84 85 let "i += 1" 86 done 87 88 89 } 90 91 92 # ============================================== 93 # main () 94 # 继续调用函数. 95 initialize 96 sift 97 print_primes 98 # 这里就是被称为结构化编程的东西. 99 # ==============================================100 101 echo102 103 exit 0104 105 106 107 # -------------------------------------------------------- #108 # 因为前面的'exit'语句, 所以后边的代码不会运行. 109 110 # 下边的代码, 是由Stephane Chazelas所编写的埃拉托色尼素数筛子的改进版本, 111 #+ 这个版本可以运行的快一些. 112 113 # 必须在命令行上指定参数(这个参数就是: 寻找素数的限制范围). 114 115 UPPER_LIMIT=$1 # 来自于命令行. 116 let SPLIT=UPPER_LIMIT/2 # 从中间值到最大值. 117 118 Primes=( '' $(seq $UPPER_LIMIT) )119 120 i=1121 until (( ( i += 1 ) > SPLIT )) # 仅需要从中间值检查. 122 do123 if [[ -n $Primes[i] ]]124 then125 t=$i126 until (( ( t += i ) > UPPER_LIMIT ))127 do128 Primes[t]=129 done130 fi 131 done 132 echo ${Primes[*]}133 134 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>上边的这个例子是基于数组的素数产生器, 还有不使用数组的素数产生器<AHREF="contributed-scripts.html#PRIMES">例子 A-16</A>, 让我们来比较一番. </P><P>--</P><P>数组可以进行一定程度上的扩展, 这样就可以模拟一些Bash原本不支持的数据结构. </P><DIVCLASS="EXAMPLE"><HR><ANAME="STACKEX"></A><P><B>例子 26-14. 模拟一个下推堆栈</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # stack.sh: 模拟下推堆栈 3 4 # 类似于CPU栈, 下推堆栈依次保存数据项, 5 #+ 但是取数据时, 却反序进行, 后进先出. 6 7 BP=100 # 栈数组的基址指针. 8 # 从元素100开始. 9 10 SP=$BP # 栈指针. 11 # 将其初始化为栈"基址"(栈底). 12 13 Data= # 当前栈的数据内容. 14 # 必须定义为全局变量, 15 #+ 因为函数所能够返回的整数存在范围限制. 16 17 declare -a stack 18 19 20 push() # 压栈. 21 {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -