Zsh 开发指南(第五篇 数组)

导读

了解完结构比较简单的字符串后,我们来看更复杂一些的数组。其实字符串在 zsh 中也可以当字符数组操作,但很少有需要把字符串当数组来处理的场景。本篇中主要讲的是字符串数组,复杂度要比单个字符串高一些。

在实际的脚本编写中,较少需要处理单个的字符串。往往需要处理从各个地方过来的大量文本,不可避免会用到数组。用好数组,会让文本处理工作事半功倍。

本篇只涉及数组的基础用法。

数组定义

数组可以直接赋值使用,不需要提前声明。等号和小括号之间不能有空格,小括号中的元素以空格隔开。

% array=(a bc ccc dddd)
# 用 $array 即可访问数组全部元素,输出时元素以空格分隔
% echo $array
a bc ccc dddd

# 使用 print -l 可以每行输出一个元素
% print -l $array
a
bc
ccc
dddd

# 输出数组中的元素个数,用法和取字符串长度一样
% echo $#array
4

# 包含带空格的字符串
% array=(a "bc ccc" dddd)
% print -l $array
a
bc ccc
dddd

# 可以换行赋值,但如果行中间有空格,依然需要加引号
% array=(
> a
> bb
> "c c c"
> dddd
> )

元素读写

% array=(a bc ccc dddd)

# 用法和取字符串的第几个字符一样,从 1 开始算
% echo $array[3]
ccc
# -1 依然是最后一个元素,-2 是倒数第二个,以此类推
% echo $array[-1]
dddd

% array[3]=CCC

# 如果赋值的内容是一个空的小括号,则删除该元素
% array[2]=()

% print -l $array
a
CCC
dddd

# 用 += 为数组添加一个新元素
% array+=eeeee
% print -l $array
a
CCC
dddd
eeeee

# 用 unset 可以删除整个数组
% unset array

# array 变量变成未定义状态
% echo $+array
0

数组拼接

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

# 用 += 拼接数组
% array1+=(e f g)
% echo $array1
a b c d e f g

# 拼接另一个数组,小括号不可以省略,否则 array1 会被转成一个字符串
% array2+=($array1)
% echo $#array2
11

# 去掉引号后,array1 被转成了一个字符串
% array2+=$array1
% echo $#array2
12
% echo $array2[12]
a b c d e f g


# 字符串可以直接拼接数组而转化成数组
% str=abcd
% str+=(1234)

% echo $#str
2

数组遍历

% 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)

% echo $array[2,3]
bb ccc

# 依然可以多对多地替换元素
% array[3,-1]=(1 2 3 4)
% echo $array
a bb 1 2 3 4

# 也可以使用另一种语法,不建议使用
% echo ${array:0:3}
a bb 1

元素查找

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

% 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

元素排序

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

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

# 降序排列,从大到小
% 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

去除重复元素

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

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

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

# 大括号中的逗号分隔的字符串会被展开
% 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

从字符串构造数组

% str="a bb ccc dddd"

# ${=str} 可以将 str 内容按空格切分成数组
% 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

从文件构造数组

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


# 从文件构造数组,并将每行按分隔符 : 分隔后输出所有列
for i (${(f)"$(<test.txt)"}) {
    array=(${(s.:.)i})
    echo $array[1,-1]
}

从文件列表构造数组

# 这里的 * 即上一篇讲的通配符,所有的用法都可以在这里使用。
% array=(/usr/bin/vim*)
% print -l $array
/usr/bin/vim
/usr/bin/vimdiff
/usr/bin/vimtutor

# 要比 ls /usr/bin/[a-b]?? | wc -l 快很多
% array=(/usr/bin/[a-b]??) && print $#array
3

数组交集差集

% 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

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

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

% 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

总结

本篇讲的是数组的基础用法,还有很多复杂的操作方法,以后会提到。

参考

http://zshwiki.org/home/scripting/array

http://www.bash2zsh.com/zsh_refcard/refcard.pdf

更新历史

20170830:增加“使用连续字符或者数值构造数组”。

20170909:修正“从字符串构造数组”中的错误。

本文不再更新,全系列文章在此更新维护:github.com/goreliu/zshguide

付费解决 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等领域相关问题,灵活定价,欢迎咨询,微信 ly50247。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 来源:NumPy Tutorial - TutorialsPoint 译者:飞龙 协议:CC BY-NC-SA 4...
    布客飞龙阅读 31,040评论 6 96
  • 数组在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称...
    朱森阅读 3,780评论 2 13
  • 数组是值的有序集合。每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引。 JavaScript...
    劼哥stone阅读 1,108评论 6 20
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,151评论 0 4
  • 作为一个黑眼圈中毒患者,即使睡眠质量赛过服了脑白金依旧是cosplay国宝100%(体形相似度高达200%)。前两...
    AlwaysGleaming阅读 277评论 0 1