如何在 Bash 中解析 CSV 數據
已發表: 2022-09-16逗號分隔值 (CSV) 文件是導出數據的最常見格式之一。 在 Linux 上,我們可以使用 Bash 命令讀取 CSV 文件。 但它會變得非常複雜,非常快。 我們會伸出援手。
什麼是 CSV 文件?
逗號分隔值文件是包含表格數據的文本文件。 CSV 是一種分隔數據。 顧名思義,逗號“ ,
”用於將每個數據字段(或值)與其相鄰字段分開。
CSV 無處不在。 如果應用程序具有導入和導出功能,它幾乎總是支持 CSV。 CSV 文件是人類可讀的。 您可以用 less 查看它們,在任何文本編輯器中打開它們,並將它們從一個程序移動到另一個程序。 例如,您可以從 SQLite 數據庫中導出數據並在 LibreOffice Calc 中打開它。
然而,即使是 CSV 也會變得複雜。 想要在數據字段中使用逗號? 該字段需要用引號“ "
括起來。要在字段中包含引號,每個引號需要輸入兩次。
當然,如果您使用由您編寫的程序或腳本生成的 CSV,則 CSV 格式可能簡單明了。 如果您被迫使用更複雜的 CSV 格式,而 Linux 就是 Linux,那麼我們也可以使用一些解決方案。
一些樣本數據
您可以使用 Online Data Generator 等網站輕鬆生成一些示例 CSV 數據。 您可以定義所需的字段並選擇所需的數據行數。 您的數據使用真實的虛擬值生成並下載到您的計算機。
我們創建了一個包含 50 行虛擬員工信息的文件:
- id :一個簡單的唯一整數值。
- firstname :人的名字。
- lastname :此人的姓氏。
- job-title :此人的職位。
- email-address :此人的電子郵件地址。
- 分公司:他們工作的公司分公司。
- state :分支所在的州。
某些 CSV 文件具有列出字段名稱的標題行。 我們的示例文件有一個。 這是我們文件的頂部:
第一行將字段名稱保存為逗號分隔值。
從 CSV 文件中解析數據
讓我們編寫一個腳本來讀取 CSV 文件並從每條記錄中提取字段。 將此腳本複製到編輯器中,並將其保存到名為“field.sh”的文件中。
#! /bin/bash 而 IFS="," 讀取 -r id firstname lastname jobtitle email 分支狀態 做 echo "記錄 ID:$id" 迴聲“名字:$名” 迴聲“姓氏:$姓氏” echo "職位名稱:$jobtitle" echo "郵箱地址:$email" 迴聲“分支:$分支” 迴聲“狀態:$狀態” 迴聲“” 完成 < <(tail -n +2 sample.csv)
我們的小腳本中有很多內容。 讓我們分解一下。
我們正在使用一個while
循環。 只要while
循環條件解析為 true,就會執行while
循環的主體。 循環的主體非常簡單。 一組echo
顯語句用於將某些變量的值打印到終端窗口。
while
循環條件比循環體更有趣。 我們使用IFS=","
語句指定應使用逗號作為內部字段分隔符。 IFS 是一個環境變量。 read
命令在解析文本序列時引用它的值。
我們使用read
命令的-r
(保留反斜杠)選項來忽略數據中可能存在的任何反斜杠。 它們將被視為常規字符。
read
命令解析的文本存儲在一組以 CSV 字段命名的變量中。 它們可以很容易地被命名為field1, field2, ... field7
,但是有意義的名字讓生活更輕鬆。
數據是作為tail
命令的輸出獲得的。 我們使用tail
是因為它為我們提供了一種跳過 CSV 文件標題行的簡單方法。 -n +2
(行號)選項告訴tail
從第二行開始讀取。
<(...)
構造稱為進程替換。 它使 Bash 接受進程的輸出,就好像它來自文件描述符一樣。 然後將其重定向到while
循環中,提供read
命令將解析的文本。
使用chmod
命令使腳本可執行。 每次從本文複製腳本時都需要這樣做。 在每種情況下替換相應腳本的名稱。
chmod +x field.sh
當我們運行腳本時,記錄被正確地拆分為它們的組成字段,每個字段存儲在不同的變量中。
./field.sh
每條記錄都打印為一組字段。
選擇字段
也許我們不想或不需要檢索每個字段。 我們可以通過合併cut
命令來獲得選擇的字段。
該腳本稱為“select.sh”。
#!/bin/bash while IFS="," 讀取 -r id jobtitle 分支狀態 做 echo "記錄 ID:$id" echo "職位名稱:$jobtitle" 迴聲“分支:$分支” 迴聲“狀態:$狀態” 迴聲“” 完成 < <(cut -d "," -f1,4,6,7 sample.csv | tail -n +2)
我們已將cut
命令添加到進程替換子句中。 我們使用-d
(分隔符)選項告訴cut
使用逗號“ ,
”作為分隔符。 -f
(字段)選項告訴cut
我們需要字段一、四、六和七。 這四個字段被讀入四個變量,這些變量被打印在while
循環的主體中。
這是我們運行腳本時得到的。
./select.sh
通過添加cut
命令,我們可以選擇我們想要的字段並忽略我們不想要的字段。
到目前為止,一切都很好。 但…
如果您處理的 CSV 不復雜,字段數據中沒有逗號或引號,那麼我們所介紹的內容可能會滿足您的 CSV 解析需求。 為了顯示我們可能遇到的問題,我們修改了一小部分數據,使其看起來像這樣。
ID、名字、姓氏、職位、電子郵件地址、分支機構、州 1,Rosalyn,Brennan,“高級管家”,[email protected],馬里蘭州明尼阿波利斯 2,丹尼,雷登,“分析師”“預算”“”,[email protected],威尼斯,北卡羅來納 3,Lexi,Roscoe,藥劑師,,Irlington,Vermont
- 記錄一在
job-title
字段中有逗號,因此該字段需要用引號引起來。 - 記錄二在
jobs-title
字段中有一個用兩組引號括起來的單詞。 - 記錄三在
email-address
字段中沒有數據。
此數據保存為“sample2.csv”。 修改“field.sh”腳本以調用“sample2.csv”,並將其保存為“field2.sh”。
#! /bin/bash 而 IFS="," 讀取 -r id firstname lastname jobtitle email 分支狀態 做 echo "記錄 ID:$id" 迴聲“名字:$名” 迴聲“姓氏:$姓氏” echo "職位名稱:$jobtitle" echo "郵箱地址:$email" 迴聲“分支:$分支” 迴聲“狀態:$狀態” 迴聲“” 完成 < <(tail -n +2 sample2.csv)
當我們運行這個腳本時,我們可以看到我們簡單的 CSV 解析器中出現了裂縫。
./field2.sh
第一條記錄將職位字段拆分為兩個字段,將第二部分視為電子郵件地址。 在此之後的每個字段都向右移動一個位置。 最後一個字段包含branch
和state
值。
第二條記錄保留所有引號。 它應該只在“預算”一詞周圍有一對引號。
第三條記錄實際上處理了它應該處理的缺失字段。 電子郵件地址丟失,但其他一切都應如此。
與直覺相反,對於簡單的數據格式,編寫一個健壯的通用 CSV 解析器是非常困難的。 像awk
這樣的工具會讓你接近,但總會有邊緣情況和例外情況發生。
嘗試編寫一個可靠的 CSV 解析器可能不是最好的方法。 另一種方法——特別是如果你在某種截止日期前工作——採用兩種不同的策略。
一種是使用專門設計的工具來操作和提取數據。 第二個是清理您的數據並替換嵌入逗號和引號等問題場景。 然後,您的簡單 Bash 解析器可以處理對 Bash 友好的 CSV。
csvkit 工具包
CSV 工具包csvkit
是一組專門用於幫助處理 CSV 文件的實用程序。 您需要將其安裝在您的計算機上。
要在 Ubuntu 上安裝它,請使用以下命令:
sudo apt install csvkit
要在 Fedora 上安裝它,您需要輸入:
須藤 dnf 安裝 python3-csvkit
在 Manjaro 上,命令是:
sudo pacman -S csvkit
如果我們將 CSV 文件的名稱傳遞給它, csvlook
實用程序會顯示一個表格,其中顯示每個字段的內容。 顯示字段內容以顯示字段內容所代表的內容,而不是存儲在 CSV 文件中的內容。
讓我們用我們有問題的“sample2.csv”文件嘗試csvlook
。
csvlook sample2.csv
所有字段均正確顯示。 這證明問題不在於 CSV。 問題是我們的腳本過於簡單,無法正確解釋 CSV。
要選擇特定列,請使用csvcut
命令。 -c
(列)選項可以與字段名或列號一起使用,或兩者結合使用。
假設我們需要從每條記錄中提取名字和姓氏、職位和電子郵件地址,但我們希望名稱順序為“姓氏,名字”。 我們需要做的就是按照我們想要的順序放置字段名稱或數字。
這三個命令都是等價的。
csvcut -c 姓氏、名字、職位、電子郵件地址 sample2.csv
csvcut -c lastname,firstname,4,5 sample2.csv
csvcut -c 3,2,4,5 sample2.csv
我們可以添加csvsort
命令來按字段對輸出進行排序。 我們使用-c
(列)選項指定要排序的列,並使用-r
(反向)選項按降序排序。
csvcut -c 3,2,4,5 sample2.csv | csvsort -c 1 -r
為了使輸出更漂亮,我們可以通過csvlook
提供它。
csvcut -c 3,2,4,5 sample2.csv | csvsort -c 1 -r | csvlook
一個巧妙的做法是,即使記錄已排序,帶有字段名稱的標題行仍保留為第一行。 一旦我們滿意,我們就可以按照我們想要的方式獲得數據,我們可以從命令鏈中刪除csvlook
,並通過將輸出重定向到一個文件來創建一個新的 CSV 文件。
我們向“sample2.file”添加了更多數據,刪除了csvsort
命令,並創建了一個名為“sample3.csv”的新文件。
csvcut -c 3,2,4,5 sample2.csv > sample3.csv
清理 CSV 數據的安全方法
如果您在 LibreOffice Calc 中打開 CSV 文件,每個字段都將放置在一個單元格中。 您可以使用查找和替換功能來搜索逗號。 您可以將它們替換為“nothing”,以便它們消失,或者使用不會影響 CSV 解析的字符,如分號“ ;
“ 例如。
您不會看到引用字段周圍的引號。 您將看到的唯一引號是字段數據中嵌入的引號。 這些顯示為單引號。 查找並用單個撇號“ '
”替換它們將替換 CSV 文件中的雙引號。
在像 LibreOffice Calc 這樣的應用程序中進行查找和替換意味著您不會意外刪除任何字段分隔符逗號,也不會刪除引用字段周圍的引號。 您只會更改字段的數據值。
我們用分號更改了字段中的所有逗號,用撇號更改了所有嵌入的引號,並保存了我們的更改。
然後我們創建了一個名為“field3.sh”的腳本來解析“sample3.csv”。
#! /bin/bash 而 IFS="," 讀取 -r lastname firstname jobtitle email 做 迴聲“姓氏:$姓氏” 迴聲“名字:$名” echo "職位名稱:$jobtitle" echo "郵箱地址:$email" 迴聲“” 完成 < <(tail -n +2 sample3.csv)
讓我們看看我們在運行它時會得到什麼。
./field3.sh
我們的簡單解析器現在可以處理我們以前有問題的記錄。
你會看到很多 CSV
CSV 可以說是最接近應用程序數據通用語言的東西。 大多數處理某種形式的數據的應用程序都支持導入和導出 CSV。 了解如何以現實和實用的方式處理 CSV 將對您有利。
相關: 9 個 Bash 腳本示例讓您開始使用 Linux