HOWTO · Linux
如何使用 Bash 逐行读取文件
我们使用 Bash 中的 read 命令来逐行读取文件。
本页内容
Bash 可以在 while 循环中使用 read 命令,一次处理文本文件的一行。输入会以流的形式处理,因此脚本无需将整个文件存入内存。使用 IFS= 保留行首和行尾的空白字符,使用 -r 保留反斜杠,并在展开该值时始终将其写作 "$line"。
使用 while 循环逐行读取文件
可靠的循环会通过输入重定向从文件读取内容。|| [[ -n $line ]] 条件还可以让循环处理文件末尾没有换行符的最后一行。循环以流的形式处理文件,并在当前 shell 中保留累加的总和。
下面的完整示例会自行创建数字输入,逐个打印数值,并将六个数字相加:1、5、6、7、8 和 10,总和为 37。示例中的可见标签和输出句子保持英文;它们会显示每个数值,并报告总和 37。
#!/usr/bin/env bash
set -u
file=$(mktemp)
trap 'rm -f "$file"' EXIT
printf '%s\n' 1 5 6 7 8 10 > "$file"
sum=0
printf 'The numbers in the file are:\n'
while IFS= read -r line || [[ -n $line ]]; do
printf '%s\n' "$line"
sum=$((sum + line))
done < "$file"
printf 'The sum of the numbers in the file is:%s\n' "$sum"
The numbers in the file are:
1
5
6
7
8
10
The sum of the numbers in the file is:37
输入重定向会让循环在当前 shell 中运行,因此累加总和等更改在循环结束后仍然可用。避免使用未加引号的展开方式,例如 $line:单词拆分和路径名展开可能会改变其中的内容。
将每一行拆分为字段
若要按分隔符拆分记录,先读取每一行原始内容,再将其字段赋给变量。使用分隔符读取时,最后一个 read 变量会接收剩余字段。此示例会创建并处理全部五条记录:Rohit-10、Harish-30、Manish-50、Kapil-10 和 Anish-20。如果文件末尾没有换行符,末行保护条件也会处理非空的最后一条记录。
#!/usr/bin/env bash
set -u
file=$(mktemp)
trap 'rm -f "$file"' EXIT
printf '%s\n' 'Rohit-10' 'Harish-30' 'Manish-50' 'Kapil-10' 'Anish-20' > "$file"
while IFS= read -r line || [[ -n $line ]]; do
IFS=- read -r name earnings <<< "$line"
printf '%s has made earnings of %s pounds today!\n' "$name" "$earnings"
done < "$file"
Rohit has made earnings of 10 pounds today!
Harish has made earnings of 30 pounds today!
Manish has made earnings of 50 pounds today!
Kapil has made earnings of 10 pounds today!
Anish has made earnings of 20 pounds today!
这里,IFS=- 将连字符设为字段赋值读取操作的分隔符。使用两个变量时,name 会接收第一个连字符之前的文本,而 earnings 会接收其余内容。打印时请给变量加引号,这样其中的内容就会作为单个值传递。
选择其他 Bash 文件读取方式
如果你希望逐行处理到达的数据,或文件可能大于可用内存,请使用 while IFS= read -r line 模式。对于普通文件,直接将文件重定向到循环即可;通过进程替换运行 cat 没有额外好处。如果循环需要读取命令生成的输出,而不是文件内容,进程替换则可能有用。
如果你的 Bash 安装提供了 mapfile,可以用它将文件各行载入数组,例如 mapfile -t lines < "$file"。当后续步骤需要按索引访问这些行或反复遍历它们,且整个文件能放入内存时,可以选择这种方式。-t 选项会移除每行末尾的换行符。mapfile 是否可用取决于 Bash 安装;若需要适用范围更广的方法,请使用流式循环。