Linuxshell笔记(4)

数组

自我感觉:数组基本操作大部分跟字符串操作差不多

数组遍历

% array1=(a bb ccc dddd)
% array2=(1 2 3)

# 用 for 可以直接遍历数组,小括号不可省略
% for i ($array1) {
> echo $i
> }
a
bb
ccc
dddd

# 小括号里可以放多个数组,依次遍历
% for i ($array1 $array2) {
> echo $i
> }
a
bb
ccc
dddd
1
2
3

记:按照正常的先后顺序,先遍历完前一个,再继续下一个,从左到右;

元素查找

数组的元素查找方法,和字符串的子字符串查找语法一样。

% array=(a bb ccc dddd ccc)

# 用小 i 输出从左到右第一次匹配到的元素位置
% echo $array[(i)ccc]
3

# 如果找不到,返回数组大小 + 1
% echo $array[(i)xxx]
6

# 用大 I 输出从右到左第一次匹配到的元素位置
% echo $array[(I)ccc]
5

# 如果找不到,返回 0
% echo $array[(I)xxx]
0

# 可以用大 I 判断是否存在元素
% (($array[(I)dddd])) && echo good
good

% (($array[(I)xxx])) && echo good


% array=(aaa bbb aab bbc)
# n:2: 从指定的位置开始查找
% echo ${array[(in:2:)aa*]}
3

记: 用小 i 输出从左到右第一次匹配到的元素位置,如果找不到,返回数组大小 + 1;

        大 I 输出从右到左第一次匹配到的元素位置,如果找不到,返回 0,可以用大 I 判断是否存在元素;

         n:p: 从指定的位置(元素p为数组的角标,zsh中第一位为1,bash中则为0)开始查找。

元素排序

% array=(aa CCC b DD e 000 AA 3 aa 22)

# 用小写字母 o 升序排列,从小到大
% echo ${(o)array}
000 22 3 aa aa AA b CCC DD e

# 用大写字母 O 降序排列,从大到小
% echo ${(O)array}
e DD CCC b AA aa aa 3 22 000

# 加 i 的话大小写不敏感
% echo ${(oi)array}
000 22 3 aa AA aa b CCC DD e


% array=(cc aaa b 12 115 90)
# 加 n 的话按数字大小顺序排
% echo ${(on)array}
12 90 115 aaa b cc

# Oa 用于反转数组元素的排列顺序
% echo ${(Oa)array}
90 115 12 b aaa cc

 

记: 1.正常排序规律:先看第一位,若相同,则比较下一位,依照ASCII码表,数字在前,字母在后。

        2.用小写字母 o 升序排列,从小到大;用大写字母 O 降序排列,从大到小。

        3.加 i 的话大小写不敏感,加 n 的话按数字大小顺序排,Oa 用于反转数组元素的排列顺序,但先数字(带数字的混合串也算)再字母串的规律仍然遵循!

去除重复元素

% array=(ddd a bb a ccc bb ddd)

% echo ${(u)array}
ddd a bb ccc

 助记:(u)->unset的首字母

使用连续字符或者数值构造数组

# 大括号中的逗号分隔的字符串会被展开
% array=(aa{bb,cc,11}) && echo $array
aabb aacc aa11

# .. 会将前后的数组连续展开
% array=(aa{1..3}) && echo $array
aa1 aa2 aa3

# 第二个 .. 后的数字是展开的间隔
% array=(aa{15..19..2}) && echo $array
aa15 aa17 aa19

# 也可以从大到小展开
% array=(aa{19..15..2}) && echo $array
aa19 aa17 aa15

# 可以添加一个或多个前导 0
% array=(aa{01..03}) && echo $array
aa01 aa02 aa03

# 单个字母也可以像数值那样展开,多个字母不行
% array=(aa{a..c}) && echo $array
aaa aab aac

# 字母是按 ASCII 码的顺序展开的
% array=(aa{Y..c}) && echo $array
aaY aaZ aa[ aa\ aa] aa^ aa_ aa` aaa aab aac


# 这些用法都可以用在 for 循环里边
% for i (aa{a..c}) {
> echo $i
> }
aaa
aab
aac

记: 1.第一个..为需要展开的数组区间,第二个..后的数字是展开的间隔;

        2.可以从大到小,也可从小到大,..不对前后大小做出规定;

        3.单个字母也可以像数值那样展开,多个字母不行;

        4.字母是按 ASCII 码的顺序展开的;

        5.可以添加一个或多个前导 0。

从字符串构造数组

% str="a bb ccc dddd"

# ${=str} 可以将 str 内容按空格切分成数组
% array=(${=str})
% print -l $array[2,3]
bb
ccc


% str="a:bb:ccc:dddd"
# 如果是其他分隔符,可以设置 IFS 环境变量指定
% IFS=:
% array=(${=str})
% print -l $array[2,3]
bb
ccc


% str="a\nbb\nccc\ndddd"
# 如果是其他分隔符,也可以用 (s:x:) 指定
% array=(${(s:\n:)str})
% print -l $array[2,3]
bb
ccc


% str="a##bb##ccc##dddd"
# 分隔符可以是多个字符
% array=(${(s:##:)str})
% print -l $array[2,3]
bb
ccc


% str="a:bb:ccc:dddd"
# 如果分隔符是 :,可以 (s.:.)
% array=(${(s.:.)str})
% print -l $array[2,3]
bb
ccc

记:1. ${=str} 可以将 str 内容按空格切分成数组;

        2.如果是其他分隔符,可以设置 IFS 环境变量指定,如IFS=:;也可以用 (s:x:) 指定,如(s:/n:),前面使用过;

        3.分隔符可以是多个字符,特别地是:如果分隔符是 :,可以 (s.:.)。

        

从文件构造数组

test.txt 内容。

a
bb
ccc
dddd

每行一个元素。

# f 的功能是将字符串以换行符分隔成数组
# 双引号不可省略,不然会变成一个字符串,引号也可以加在 ${ } 上
% array=(${(f)"$(<test.txt)"})
% print -l $array
a
bb
ccc
dddd

# 不加引号的效果
% array=(${(f)$(<test.txt)})
% print -l $array
a bb ccc dddd

 记:f 的功能是将字符串以换行符分隔成数组双引号不可省略,不然会变成一个字符串,引号也可以加在 ${ } 上;不加引号就不会将换行符转义,将所有元素显示在一行。

数组交集差集

% array1=(1 2 3)
% array2=(1 2 4)

# 两个数组的交集,只输出两个数组都有的元素
% echo ${array1:*array2}
1 2

# 两个数组的差集,只输出 array1 中有,而 array2 中没有的元素
% echo ${array1:|array2}
3

# 如果有重复元素,不会去重
% array1=(1 1 2 3 3)
% array2=(4 4 1 1 2 2)
% echo ${array1:*array2}
1 1 2

记:  :*——>求交集  :|——>求差集,前者为集合本身即操作对象,后者为差集的计算对象

        如果有重复元素,不会去重。

数组交叉合并

% array1=(a b c d)
% array2=(1 2 3)

# 从 array1 取一个,再从 array2 取一个,以此类推,一个数组取完了就结束
% echo ${array1:^array2}
a 1 b 2 c 3

# 如果用 :^^,只有一个数组取完了的话,继续从头取,直到第二个数组也取完了
% echo ${array1:^^array2}
a 1 b 2 c 3 d 1

记: a1:^a2——>先从数组a1中取一个,再从数组a2中取一个,以此类推;

        a1:^^a2——>大致上和上面的差不多,就是只有一个数组取完了的话,继续从头取,直到第二个数组也取完了。

对数组中的字符串进行统一的处理

一些处理字符串的方法(主要是各种形式的截取、替换、转换等等),也可以用在数组上,效果是对数组中所有元素统一处理。

% array=(/a/b.htm /a/c /a/b/c.txt)

# :t 是取字符串中的文件名,可以用在数组上,取所有元素的文件名
% print -l ${array:t}
b.htm
c
c.txt

# :e 是取扩展名,如果没有扩展名,结果数组中不会添加空字符串
% print -l ${array:e}
htm
txt

# 字符串替换等操作也可以对数组使用,替换所有字符串
% print -l ${array/a/j}
/j/b.txt
/j/c
/j/b/c.txt

:# 也可以在数组上用,但更实用一些。

% array=(aaa bbb ccc)

# :# 是排除匹配到的元素,类似 grep -v
% print ${array:#a*}
bbb ccc

# 前边加 (M),是反转后边的效果,即只输出匹配到的元素,类似 grep
% print ${(M)array:#a*}
aaa

# 多个操作可以同时进行,(U) 是把字符串转成大写字母
% print ${(UM)array:#a*}
AAA

记: :t 是取字符串中的文件名,可以用在数组上,取所有元素的文件名;

        :e 是取扩展名,如果没有扩展名,结果数组中不会添加空字符串

        :# 是排除匹配到的元素,类似 grep -v;

        前边加 (M),是反转后边的效果,即只输出匹配到的元素;

        多个操作可以同时进行,(U) 是把字符串转成大写字母。

哈希表

     哈希表是比数组更复杂的数据结构,在某些语言里被称作关联数组或者字典等等。简单说,哈希表用于存放指定键(key)对应的值(value),键和值的关系,就像字典中单词和释义的对应关系,通过单词可以快速找到释义,而不需要从头依次遍历匹配。

哈希表定义

和其他变量类型不同,哈希表是需要提前声明的因为哈希表的赋值语法和数组一样,如果不声明,是无法区分的

% typeset -A table
# 或者用 local,二者功能是一样的
% local -A table

# 赋值的语法和数组一样,但顺序依次是键、值、键、值
% table=(k1 v1 k2 v2)

# 直接用 echo 只能输出值
% echo $table
v1 v2

# 使用 (kv) 同时输出键和值,(kv) 会把键和值都放到同一个数组里
% echo ${(kv)table}
k1 v1 k2 v2

# 哈希表的大小是键值对的数量
% echo $#table
2

 记:哈希表需声明,关键词为typeset -A或者local -A,之后的赋值操作和数组一样,奇数位则为key,偶数位为value;

元素读写

读写哈希表的方法和数组类似,只是用于定位的数字变成了字符串

# 可以声明和赋值写到一行
% local -A table=(k1 v1 k2 v2 k3 v3)
% echo $table[k2]
v2

% table[k2]="V2"

# 删除元素的方法和数组不同,引号不能省略
% unset "table[k1]"
% echo ${(kv)table}
k2 V2 k3 v3

注:删除元素的方法和数组不同,引号不能省略; 

哈希表拼接

# 追加元素的方法和数组一样
% table+=(k4 v4 k5 v5)
% echo $table
V2 v3 v4 v5


% local -A table1 table2
% table1=(k1 v1 k2 v2)
% table2=(k2 v222 k3 v3)

# 拼接哈希表,要展开成数组再追加
% table1+=(${(kv)table2})
# 如果键重复,会直接替换值,哈希表的键是不重复的
% echo ${(kv)table1}
k1 v1 k2 v222 k3 v3

注:1.拼接哈希表,要展开成数组再追加;2.如果键重复,会直接替换值,哈希表的键是不重复的。

哈希表遍历

用 (kv) (k) 等先将哈希表转化成数组,然后再遍历。

% local -A table=(k1 v1 k2 v2 k3 v3)

# 只遍历值
% for i ($table) {
> echo $i
> }
v1
v2
v3

# 只遍历键
% for i (${(k)table}) {
> echo $i
> }
k1
k2
k3

# 同时遍历键和值
% for k v (${(kv)table}) {
> echo "$k -> $v"
> }
k1 -> v1
k2 -> v2
k3 -> v3

元素查找

判断键是否存在。

% local -A table=(k1 v1 k2 v2 k3 v3)
% (($+table[k1])) && echo good
good
% (($+table[k4])) && echo good

如果需要判断某个值是否存在,直接对值的数组判断即可。但这样做就体现不出哈希表的优势了。

% local -A table=(k1 v1 k2 v2 k3 v3)
# value 是值的数组,也可以用 local -a 强行声明为数组
% value=($table)

% (( $value[(I)v1] )) && echo good
good
% (( $value[(I)v4] )) && echo good

 

元素排序

对哈希表元素排序的方法,和数组类似,多了 k v 两个选项其余的选项如 o(升序)、O(降序)、n(按数字大小)、i(忽略大小写)等通用,不再一一举例。

% local -A table=(aa 33 cc 11 bb 22)

# 只对值排序
% echo ${(o)table}
11 22 33

# 只对键排序
% echo ${(ok)table}
aa bb cc

# 键值放在一起排序
% echo ${(okv)table}
11 22 33 aa bb cc

:# 也可以在哈希表上用。

% local -A table=(k1 v1 k2 v2 k3 v3)

# 排除匹配到的值
% echo ${table:#v1}
v2 v3

# 只输出匹配到的键
% echo ${(Mk)table:#k[1-2]}
k1 k2

多维哈希表

Zsh 并不支持多维哈希表以及多维数组,但可以通过一些方法来模拟,以实现一部分功能。

用一维哈希表模拟多维哈希表
% local -A table
# 这里用 , 作为分隔符,也可以用其他符号。
% table[1,1]=a
% table[1,2]=b
% table[k,v]=c
% echo $table[1,1] $table[1,2] $table[k,v]
a b c

好处:使用方便,而且支持的维数不受限制。

坏处:功能太单一,比如不能对 table[1] 进行处理。

用字符串分割访问来模拟多维哈希表
% local -A table
# 分隔符为空格
% table[1]='a b'
% table[2]='c d'
% print -l $table[1] ${table[1][(w)2]} ${table[2][(w)1]}
a b
b
c

# 分隔符不是空格
% table[a]='aa,bb'
% table[b]='cc,dd'
% print -l $table[a] ${table[a][(ws:,:)2]} ${table[b][(ws:,:)1]}
aa,bb
bb
cc

好处:可以对 table[1] 进行处理。

坏处:不大方便,性能也不好。而且功能同样受限,比如第一维只能是数组,不能是哈希表。可以支持更多维,但需要再增加新的分隔符,使用起来更麻烦。

 

 

 

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值