簡體   English   中英

用於對 csv 文件中的行進行排序的 Bash 腳本

[英]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"

假設:

  • id 是任意字符串,一般排序(例如字典順序)不起作用。
  • id 和字段不包含空格並由空格分隔。

那么怎么樣:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM