bash提供了兩個內置命令:readarray和mapfile,它們是同義詞。它們的作用是從標准輸入讀取一行行的數據,然后每一行都賦值給一個數組的各元素。顯然,在shell編程中更常用的是從文件、從管道讀取,不過也可以從文件描述符中讀取數據。
需要先說明的是,shell並不像其它專門的編程語言對數組、列表提供了大量的操作工具,反而直接操作文本文件更為常見(sed、awk等),所以mapfile用的並不多。
1.語法
mapfile [OPTIONS] ARRAY
readarray [OPTIONS] ARRAY
其中options:
-O INDEX :指定從哪個索引號開始存儲數據,默認存儲數據的起始索引號為0
-n count :最多只拷貝多少行到數組中,如果count=0,則拷貝所有行
-s count :忽略前count行不讀取
-c NUM :每讀取NUM行就調用一次"-C callback"選項指定的callback程序
-C callback:每讀取"-c NUM"選項指定的NUM行就執行一次callback回調程序
-d string :指定讀取數據時的行分隔符,默認是換行符
-t :移除尾隨行分隔符,默認是換行符
-u fd :指定從文件描述符fd而非標准輸入中讀取數據
- 如果不指定ARRAY參數,則默認使用數組MAPFILE
- 如果不指定"-O"選項,則在存儲數據之前先清空數組(如果該數組已存在)
- 給定了"-C callback"卻沒有給定"-c NUM"時,則默認為每5000行調用一次回調程序
- 回調程序是在讀取給定行數之后,賦值到數組元素之前執行的。所以流程為:"讀NUM行-->callback-->賦值"
- 每次調用回調函數時,都將調用callback之前的最后一行數據及其對應的索引號作為回調程序的參數。例如
-c 3 -C callback
,則會將索引號2和第3行內容,索引號5和第6行內容作為callback程序的參數 - "-t"去除行尾分隔符,一般來說都是換行符。用其他語言編程過的人都知道行尾換行符有多煩心,但對於shell編程來說,倒是無所謂
2.幾個示例和注意事項
先創建一個示例用的文件alpha.log,每行一個小寫字母,共26行:
$ echo {a..z} | tr " " "\n" >alpha.log
$ cat alpha.log
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
z
讀取該文件並將每一行存儲到數組myarr中(如果不指定,則存儲到默認的MAPFILE數組中)。
$ mapfile myarr <alpha.log
$ echo ${myarr[@]}
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 z
$ echo ${myarr[2]}
c
既然是讀取標准輸入,常見的就有以下幾種讀取形式:
$ mapfile myarr <alpha.log # 1.輸入重定向
$ mapfile myarr < <(cat alpha.log) # 2.進程替換
$ cat alpha.log | mapfile myarr # 3.管道傳遞
第1、2種寫法沒什么問題,但第3種寫法是有問題的。
$ cat alpha.log | mapfile myarr1
$ echo ${#myarr1[@]}
0
從結果中可以看到,myarr1根本就不存在。為什么?我在shell中while循環的陷阱中給出過解釋。這里簡單說明一下,對於管道組合的多個命令,它們都會放進同一個進程組中,會進入子shell執行相關操作。當執行完畢后,進程組結束,子shell退出。而子shell中設置的環境是不會粘滯到父shell中的(即不會影響父shell),所以myarr1數組是子shell中的數組,回到父shell就消失了。
解決方法是在子shell中操作數組:
$ cat alpha.log | { mapfile myarr1;echo ${myarr1[@]}; }
mapfile可以指定每讀取多少行就執行一次的回調函數,並且會將執行回調函數時讀取的最后一行和對應的索引號傳遞給回調函數作為它額外的參數。
一個簡單的示例,每讀取3行就執行一次echo,注意看下面傳遞給給echo的參數值。
$ mapfile -c 3 -C "echo" myarr <alpha.log
2 c
5 f
8 i
11 l
14 o
17 r
20 u
23 x
這里的echo就是回調函數。輸出結果中每執行一次就有一空行,這是因為文件中數據是分行的,而echo又自帶換行功能。所以,可以使用"-t"選項,在每次讀取一行后就去掉該行的換行符。
$ mapfile -t -c 3 -C "echo" myarr <alpha.log
2 c
5 f
8 i
11 l
14 o
17 r
20 u
23 x
可以寫一個腳本,或者定義一個函數作為回調程序,實現更復雜的功能,但一定要注意,mapfile傳遞給callback的兩個參數總是最后兩個參數。例如:
$ myecho(){ echo $@; };mapfile -t -c 3 -C "myecho haha" myarr <alpha.log
haha 2 c
haha 5 f
haha 8 i
haha 11 l
haha 14 o
haha 17 r
haha 20 u
haha 23 x
還可以將多個操作組合起來作為一個回調程序:
$ mapfile -t -c 3 -C "echo haha;echo" myarr<alpha.log
haha
2 c
haha
5 f
haha
8 i
haha
11 l
haha
14 o
haha
17 r
haha
20 u
haha
23 x