[英]Bash script to sort rows in a csv file
我有一堆行,行中的每個字段都有一個標識該字段的標題。 目前該文件只是一個 csv,盡管前幾個字段在放入 excel 時會對齊,但由於某些行沒有某些字段或字段亂序,因此該行的其余部分未對齊。 我試圖讓每個字段在復制到 excel 並使用“文本到列”工具時與正確的列標題對齊。 我確信這意味着用相應數量的逗號填充行中的位置,以確保有足夠的空白單元格將數據字段與正確的列對齊。
Input:
id1,id2,id3,id4,id5,id6,id7,id8
id1 field1,id2 field2,id3 field3,id8 field8,id5 field5,id6 field6,id7 field7,id4 field4
id1 field1,id6 field6,id3 field3,id4 field4,id5 field5,id2 field2,id8 field8
id1 field1,id4 field4,id7 field7,id6 field6,id5 field5,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id4 field4,id2 field2,id5 field5,id6 field6,id8 field8
id1 field1,id2 field2,id8 field8,id4 field4,id5 field5,id6 field6,id7 field7,id3 field3
Output:
id1,id2,id3,id4,id5,id6,id7,id8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,,,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,,id7 field7,id8 field8
基本上,我試圖根據標題行對行重新排序,然后用額外的逗號填充該特定行中不存在應存在的字段。 每個字段在實際數據之前都有一個標簽,該標簽對應於該字段應位於的標題。
我在谷歌上找不到任何東西,我不知道如何做到這一點。 抱歉,不能再具體了。
使用 awk 運行的新數據集:
Input:
id1,id2,id3,id4
id1.100 "field1",id2.100 "field2",id3.100 "field3",id4.100 "field4"
id1.101 "field1",id2.101 "field2",id3.101 "field3",id4.101 "field4"
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.102 "field4"
id1.103 "field1",id2.103 "field2",id3.103 "field3",id4.103 "field4"
output:
id1,id2,id3,id4
,,,
,,,
,,,
,,,
不知道為什么這樣做。 新數據集在每個字段的引號內確實有 "/" ":" "(" 字符。id 部分中“.”后面的數字在我將通過此腳本推送的每個數據集之間發生變化。
我只是試過這個:
Input:
id1.100,id2.100,id3.100,id4.100
id1.100 "field1",id2.100 "field2",id3.100 "field3",id4.100 "field4"
id1.101 "field1",id2.101 "field2",id3.101 "field3",id4.101 "field4"
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.102 "field4"
id1.103 "field1",id2.103 "field2",id3.103 "field3",id4.103 "field4"
output:
id1,id2,id3,id4
id1.100 "field1",id2.100 "field2",id3.100 "field3",id4.100 "field4"
,,,
,,,
,,,
那么有沒有辦法只通過開頭來識別 id 字段? 就像 id 字段是 Name.105 一樣,只能通過“名稱”字符串來標識它?
數據集中的重復字段:
Input:
id1.100,id2.100,id3.100,id4.100
id1.100 "field1",id2.100 "field2",id3.100 "field3",id3.100 "field3",id2.100 "field2"
id1.101 "field1",id2.101 "field2",id2.101 "field2",id3.101 "field3",id3.101 "field3"
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.103 "field4",id1.102 "field1"
Output:
id1.100,id2.100,id3.100,id4.100
id1.100 "field1",id2.100 "field2",id3.100 "field3",
id1.101 "field1",id2.101 "field2",id3.101 "field3",
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.103 "field4"
假設:
那么怎么樣:
declare -A id2val # associative array to store id and fields
while IFS=, read -ra f; do
if ((nr++ == 0)); then # header line
ids=("${f[@]}") # assign ids in order
(IFS=,; echo "${ids[*]}")
# print the header
else
id2val=() # empty the associative array
for ((i=0; i<${#f[@]}; i++)); do
# process each field of the input line
id=${f[i]% *} # extract the substring before space
val=${f[i]#* } # extract the substring after space
id2val[$id]="$val" # associate field value with the id
done
for ((i=0; i<${#ids[@]}; i++)); do
# process in the "id" order
id=${ids[i]} # retrieve the id
if [[ -n ${id2val[$id]} ]]; then
# found the field associated to the id
list[i]="$id ${id2val[$id]}"
# then format the csv field as output
else
list[i]=""
fi
done
(IFS=,; echo "${list[*]}")
# print the record as csv
fi
done < input.csv
輸出:
id1,id2,id3,id4,id5,id6,id7,id8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,,,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
在處理每條記錄時,它首先將空格上的每一列拆分為 id 和字段值,然后將它們存儲在關聯數組中。 接下來,它以標頭定義的順序循環遍歷 id; 如果找到與 id 關聯的字段值,則填充 fileld 以輸出。
如果沒有關聯數組,我們將需要創建一個雙循環來匹配 id,這將是低效的。
如果awk
是您的選擇,您還可以說:
awk 'BEGIN {FS=OFS=","}
NR==1 {
for (i=1; i<=NF; i++) ids[i] = $i
nf = NF
print
next
}
{
delete id2val
for (i=1; i<=NF; i++) {
split($i, a, " ")
id2val[a[1]] = a[2]
}
for (i=1; i<=nf; i++) {
id = ids[i]
$i = (id2val[id] != "") ? id " " id2val[id] : ""
}
print
}
' input.csv
這將比bash
解決方案更有效。
[更新]
修改以滿足 OP 提供的新數據集。
新數據失敗,因為原始awk
腳本期望標題行中的 ID 和留到空格和“字段”的 ID 格式相同,而不會將點視為特殊含義。
請您嘗試以下操作:
awk 'BEGIN {FS=OFS=","}
NR==1 {
print
for (i=1; i<=NF; i++) {
sub("\\..*", "", $i) # remove the suffix, if any
ids[i] = $i
}
nf = NF
next
}
{
delete id2val
for (i=1; i<=NF; i++) {
split($i, a, " ")
split(a[1], b, ".") # splits the id on "." if any
id2id[b[1]] = a[1] # maps "id1" to "id1.100" e.g.
id2val[b[1]] = a[2] # maps "id1" to "field1" e.g.
}
for (i=1; i<=nf; i++) {
id = ids[i]
$i = (id2val[id] != "") ? id2id[id] " " id2val[id] : ""
}
NF = nf # adjust the NF to "print" properly
print
}
' input.csv
我修改了awk
腳本以拆分點上的 ID,並引入了一個變量id2id
來檢索原始(包括點和數字)ID 字符串。
更新后的腳本與原始數據集兼容,原始數據集的 ID 不包含點,並且無論字段中的/.(
等字符如何,都可以正常工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.