導讀
瞭解完結構比較簡單的字串後,我們來看更復雜一些的陣列。其實字串在 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: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複製程式碼
從檔案構造陣列
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複製程式碼
總結
本篇講的是陣列的基礎用法,還有很多複雜的操作方法,以後會提到。
參考
更新歷史
20170830:增加“使用連續字元或者數值構造陣列”。
20170909:修正“從字串構造陣列”中的錯誤。
20170910:增加“從字串構造陣列”中的部分內容。
全系列文章地址:github.com/goreliu/zsh…
付費解決 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等領域相關問題,靈活定價,歡迎諮詢,微信 ly50247。