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 選項會移除每行結尾的換行符號。是否能使用此命令取決於 Bash 安裝;若需要適用範圍較廣的做法,請使用串流迴圈。