mapfile
或 readarray
是 Bash shell 中的一个内置命令,主要用于从标准输入或文件中读取行到数组变量中。
mapfile
和 readarray
是完全相同的命令。在 Bash shell 中,这两个命令是等效的,可以互换使用。
mapfile --help
root@ky:/userdata/tmp# mapfile --help
mapfile: mapfile [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]
Read lines from the standard input into an indexed array variable.
Read lines from the standard input into the indexed array variable ARRAY, or
from file descriptor FD if the -u option is supplied. The variable MAPFILE
is the default ARRAY.
Options:
-d delim Use DELIM to terminate lines, instead of newline
-n count Copy at most COUNT lines. If COUNT is 0, all lines are copied
-O origin Begin assigning to ARRAY at index ORIGIN. The default index is 0
-s count Discard the first COUNT lines read
-t Remove a trailing DELIM from each line read (default newline)
-u fd Read lines from file descriptor FD instead of the standard input
-C callback Evaluate CALLBACK each time QUANTUM lines are read
-c quantum Specify the number of lines read between each call to
CALLBACK
Arguments:
ARRAY Array variable name to use for file data
If -C is supplied without -c, the default quantum is 5000. When
CALLBACK is evaluated, it is supplied the index of the next array
element to be assigned and the line to be assigned to that element
as additional arguments.
If not supplied with an explicit origin, mapfile will clear ARRAY before
assigning to it.
Exit Status:
Returns success unless an invalid option is given or ARRAY is readonly or
not an indexed array.
root@ubuntu:~# mapfile --help
mapfile: mapfile [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]
从标准输入读取行到索引数组变量。
从标准输入读取行到索引数组变量ARRAY,或者
如果提供了-u选项,则从文件描述符FD读取。变量MAPFILE
是默认的ARRAY。
选项:
-d delim 使用DELIM终止行,而不是换行
-n count 最多复制COUNT行。如果COUNT为0,所有行都被复制
-O origin 从索引ORIGIN开始分配给ARRAY。默认索引是0
-s count 丢弃首先读取的COUNT行
-t 从每一行读取中删除尾随的DELIM(默认为换行)
-u fd 从文件描述符FD读取行,而不是标准输入
-C callback 每读取QUANTUM行就评估一次CALLBACK
-c quantum 指定每次调用CALLBACK之间读取的行数
参数:
ARRAY 用于存储文件数据的数组变量名
如果没有提供-c,-C的默认量子为5000。当
评估CALLBACK时,将提供下一个要分配的数组元素的索引
和要分配给该元素的行作为额外的参数。
如果没有提供显式的起始,mapfile会在分配给ARRAY之前清除它。
退出状态:
除非给出无效选项或ARRAY是只读的或者不是索引数组,否则返回成功。
首先,我们创建一个文本文件,其中包含一些我们想要处理的数据。打开终端并输入以下内容:
echo -e "line1\nline2\nline3\nline4\nline5" > test.txt
这将在你的当前目录下创建一个名为test.txt的文件,并填充了5行数据。
然后,我们可以使用mapfile命令读取这个文件的内容并存储到一个数组中。在终端中输入:
mapfile -t my_array < test.txt
这里,-t
选项用于删除每行的尾随换行符,my_array
是我们的数组名称,< test.txt
表示从test.txt文件中读取数据。
现在,我们可以打印数组的内容来验证mapfile命令是否正常工作。在终端中输入:
printf "%s\n" "${my_array[@]}"
printf
:这是一个在shell脚本中常用的命令,用于格式化并打印数据。它的功能和C语言中的printf函数类似。
"%s\n"
:这是printf命令的格式字符串。%s表示一个字符串,\n表示换行符。所以,这个格式字符串的意思是,对于每个参数,打印出该参数(视为字符串)并在其后添加一个换行符。
"${my_array[@]}"
:这是bash中获取数组所有元素的语法。my_array是数组名,[@]表示获取数组中的所有元素。${}是变量替换符,它告诉bash将my_array[@]替换为my_array数组中的所有元素。综合起来,
printf "%s\n" "${my_array[@]}"
这条命令的作用是,按照每行一个的方式打印出my_array数组中的所有元素。
你应该会看到test.txt文件中的每一行都被打印出来。
以下是mapfile命令中每个选项的使用示例:
-d delim
是mapfile命令的一个选项,其中delim是你自定义的终止符。默认情况下,mapfile会将每一行(由换行符’\n’分隔)读入数组。但是,如果你使用了-d
选项,mapfile就会用你指定的delim作为行的结束符,而不是使用默认的换行符。
举个例子,假设你有以下的字符串:
a:b:c
默认情况下,mapfile会将这整个字符串视为一个行,因为没有遇到换行符’\n’。但是,如果你执行mapfile -d : my_array
,然后输入上述字符串,mapfile就会将’a’, ‘b’, ‘c’视为三行,因为此时它把’:'作为行的结束符。
所以说,-d delim
选项允许你改变mapfile识别行结束的方式。
mapfile -d : -t my_array < <(echo -e "a:b:c\nd:e:f")
printf "%s\n" "${my_array[@]}"
这将打印出:
a
b
c
d
e
f
mapfile -t -n 2 my_array < <(echo -e "line1\nline2\nline3")
printf "%s\n" "${my_array[@]}"
这将打印出:
line1
line2
-O origin
是mapfile命令的一个选项,用于指定数组索引的起始值。
默认情况下,当你使用mapfile读取文件时,它会将文件的每一行存入到一个数组中,其中第一行对应数组的索引0,第二行对应索引1,依此类推。这就是默认索引是0的意思。
然而,如果你使用 -O origin
选项,你可以改变这个默认行为。origin是一个数字,代表你想要数组的起始索引值。例如,如果你执行 mapfile -O 3 -t my_array < test.txt
,那么文件的第一行将会被存储在my_array[3],第二行存储在my_array[4],以此类推。
换句话说,-O origin
允许你控制数据被存储到数组的哪个位置开始。
mapfile -t -O 2 my_array < <(echo -e "line1\nline2\nline3")
printf "%s\n" "${my_array[2]}" "${my_array[3]}" "${my_array[4]}"
这将打印出:
line1
line2
line3
mapfile -t -s 1 my_array < <(echo -e "line1\nline2\nline3")
printf "%s\n" "${my_array[@]}"
这将打印出:
line2
line3
mapfile -t my_array < <(echo -e "line1\nline2\n")
printf "%s\n" "${my_array[@]}"
这将打印出:
line1
line2
如果不加-t
参数,打印出的是:
line1
line2
exec 3< test.txt
mapfile -t -u 3 my_array
printf "%s\n" "${my_array[@]}"
exec 3< test.txt
是在Bash shell中创建并打开一个新的文件描述符(在这里是描述符3)并将其与特定文件(在这里是test.txt)关联起来的方法。具体来说:
exec
是一个bash内建命令,通常用于启动新的进程。然而,在这个上下文中,它被用来打开、关闭或复制文件描述符。
3
是你选择的文件描述符号码。在Linux和Unix系统中,文件描述符0、1和2分别代表标准输入、标准输出和标准错误。数字3及以上可以被程序用来创建额外的文件描述符。
<
是输入重定向符号。它将文件内容导向某个位置(在这个例子中,是文件描述符3)。
test.txt
是你想要读取的文件名。因此,
exec 3< test.txt
的效果是:打开文件test.txt,并将它与文件描述符3关联。之后,你可以使用这个文件描述符来读取文件内容。例如,你可以使用命令read -u 3 line
来从文件描述符3(也就是test.txt文件)读取一行。
这会从myfile.txt文件中读取行到my_array。
-C callback
是mapfile命令的一个选项,它允许你在读取指定数量的行之后执行一个特定的回调函数。
这里的callback是你定义的一个bash函数,而QUANTUM是一个数字,表示每读取多少行就执行一次这个函数。例如,如果你设置 -C myfunc -c 5
,那么每当mapfile读取了5行数据后,就会执行myfunc这个函数。
这个功能在处理大文件时非常有用,因为你可以定期处理数据,而不必等到所有数据都被读取完毕。例如,你可以使用回调函数来显示进度信息,或者将数据分批写入到另一个文件中。
请注意,回调函数应该在使用mapfile之前定义,并且这个函数可以访问到两个变量:MAPFILE和MAPFILE_COUNT。MAPFILE是一个数组,包含已经被读取的行,而MAPFILE_COUNT表示已经读取的行数。
当然,下面是一个使用-C callback
选项的完整示例:
首先,我们创建一个名为process_lines
的bash函数。这个函数将会在每读取到5行数据时被调用,并打印出已读取的行数和最后一行的内容。
process_lines() {
local index=$1
local line=$2
echo "已读取到第$index行"
echo "该行内容: $line"
}
然后,我们创建一个测试文件并使用mapfile命令读取它:
echo -e "line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10" > test.txt
mapfile -t -C process_lines -c 5 my_array < test.txt
在这个命令中,-C process_lines
指定了回调函数,而-c 5
指定了每读取5行就执行一次回调函数。
如果你运行上述代码,你应该会看到以下的输出:
已读取到第4行
该行内容: line5
已读取到第9行
该行内容: line10
这就说明,在读取到第4行和第9行时,process_lines
函数被成功地调用了。
mapfile -C callback -c 100 my_array < test.txt
这将在读取myfile.txt的每100行后执行callback函数。
如下两行命令:
echo -e "a:b:c\nd:e:f" | mapfile -d : -t my_array2
printf "%s\n" "${my_array2[@]}"
执行后显示为空:
原因是在管道操作中使用mapfile命令时,由于shell的子进程问题,mapfile无法改变父shell环境中的数组。
要解决这个问题,可以尝试避免使用管道,而是使用输入重定向(<)将echo的结果传给mapfile,如下所示:
mapfile -d : -t my_array < <(echo -e "a:b:c\nd:e:f")
printf "%s\n" "${my_array[@]}"
这里我们使用了一个叫做进程替代(process substitution)的特性,< <(commands)
的形式会创建一个临时文件(或者在某些系统中是一个文件描述符),这个文件包含commands命令的输出,然后这个文件会被作为输入重定向到前面的命令中。
这样应该能够得到预期的结果,即数组my_array包含"a", “b”, “c”, “d”, “e”, "f"这六个元素。
参考文章:linux shell编程、bash编程、shell教程、bash教程、shell文档、bash文档、shell脚本、bash脚本【教程】第三章:特殊字符