簡體   English   中英

使用join / awk / sed合並CSV文件

[英]Merge CSV files using join/awk/sed

您能幫我找到bash命令,它將以下cvs文件“ template.csv + file1.csv + file2.csv + file3.csv + ... + fileX.csv”合並/合並到“ ouput.csv”中。

對於template.csv中的每一行,將在fileX.csv中列出的關聯值(如果存在)連接起來,如下所示:

template.csv:

header
1
2
3
4
5
6
7
8
9

file1.csv:

header,value1
2,value12
3,value13
7,value17
8,value18
9,value19

file2.csv:

header,value2
1,value21
2,value22
3,value23
4,value24

file3.csv:

header,value3
2,value32
4,value34
6,value36
7,value37
8,value38

output.csv:

header,value1,value2,value3
1,,value21,
2,value12,value22,value32
3,value13,value23,
4,,value24,value34
5,,,
6,,,value36
7,value17,,value37
8,value18,,value38
9,value19,,

我的模板文件包含35137行。
我已經開發了一個執行此合並的bash腳本(基於“ do while”等),但性能根本不佳。 太長,無法輸出output.csv。 我敢肯定可以使用join,awk,...來做同樣的事情,但是我不知道如何...

重要更新

我的真實文件的第一列包含一個日期時間,而不是一個簡單的數字...因此該腳本必須考慮到日期和時間之間的空格...抱歉更新!

現在應使用以下csv文件作為示例來設計腳本:

template.csv:

header
2000-01-01 00:00:00
2000-01-01 00:15:00
2000-01-01 00:30:00
2000-01-01 00:45:00
2000-01-01 01:00:00
2000-01-01 01:15:00
2000-01-01 01:30:00
2000-01-01 01:45:00
2000-01-01 02:00:00

file1.csv:

header,value1
2000-01-01 00:15:00,value12
2000-01-01 00:30:00,value13
2000-01-01 01:30:00,value17
2000-01-01 01:45:00,value18
2000-01-01 02:00:00,value19

file2.csv:

header,value2
2000-01-01 00:00:00,value21
2000-01-01 00:15:00,value22
2000-01-01 00:30:00,value23
2000-01-01 00:45:00,value24

file3.csv:

header,value3
2000-01-01 00:15:00,value32
2000-01-01 00:45:00,value34
2000-01-01 01:15:00,value36
2000-01-01 01:30:00,value37
2000-01-01 01:45:00,value38

output.csv:

header,value1,value2,value3
2000-01-01 00:00:00,,value21,
2000-01-01 00:15:00,value12,value22,value32
2000-01-01 00:30:00,value13,value23,
2000-01-01 00:45:00,,value24,value34
2000-01-01 01:00:00,,,
2000-01-01 01:15:00,,,value36
2000-01-01 01:30:00,value17,,value37
2000-01-01 01:45:00,value18,,value38
2000-01-01 02:00:00,value19,,
$ cat tst.awk
BEGIN { FS=OFS="," }
NR == FNR { key[++numRows] = $1 }
{ fld[$1,ARGIND] = $NF }
END {
    for (rowNr=1; rowNr<=numRows; rowNr++) {
        for (colNr=1; colNr<=ARGIND; colNr++) {
            printf "%s%s", fld[key[rowNr],colNr], (colNr<ARGIND ? OFS : ORS)
        }
    }
}

$ awk -f tst.awk template.csv file1.csv file2.csv file3.csv
header,value1,value2,value3
2000-01-01 00:00:00,,value21,
2000-01-01 00:15:00,value12,value22,value32
2000-01-01 00:30:00,value13,value23,
2000-01-01 00:45:00,,value24,value34
2000-01-01 01:00:00,,,
2000-01-01 01:15:00,,,value36
2000-01-01 01:30:00,value17,,value37
2000-01-01 01:45:00,value18,,value38
2000-01-01 02:00:00,value19,,

上面的代碼將GNU awk用於ARGIND ,而其他awks只是添加了一行,內容為FNR==1 { ++ARGIND }

這應該工作(為便於解釋,請閱讀注釋):

#!/bin/sh

awk -F, -v file=0 '
  FNR == 1 {                     # first line in the file
    if(file == 0) {              # if in first file (template.csv):
      header = $1                # init header
    } else {
      header = header "," $2     # else append field name
    }
    next                         # forward to next line.
  }
  file == 0 {                    # if in first file:
    key[FNR] = $1                # remember key
    next                         # next line.
  }
  {
    field[$1][file] = $2         # otherwise: remember field
  }
  ENDFILE {                      # at the end of a file:
    file = file + 1              # increase counter
  }
  END {                          # in the end, assemble and
    print header                 # print lines.
    asort(key)
    for(k in key) {
      line = ""
      for(i = 1; i < file; ++i) {
        line = line "," field[key[k]][i]
      }
      print key[k] line
    }
  }
  ' template.csv file1.csv file2.csv file3.csv

您可以使用多個呼叫join

join -t , -a 1 -o auto template.csv file1.csv | join -t , -a 1 -o auto - file2.csv | join -t , -a 1 -o auto - file3.csv

或更清晰:

alias myjoin='join -t , -a 1 -o auto'
myjoin template.csv file1.csv | myjoin - file2.csv | myjoin - file3.csv

說明:

  • -t ,指定字段分隔符( ,
  • -a 1指示打印來自第一個文件的不可配對的行(假設頭文件包含所有可能的頭)
  • -o auto自動控制格式,是打印空白字段所必需的

證明:

$ join -t , -a 1 -o auto template.csv file1.csv | join -t , -a 1 -o auto - file2.csv | join -t , -a 1 -o auto - file3.csv
header,value1,value2,value3
2000-01-01 00:00:00,,value21,
2000-01-01 00:15:00,value12,value22,value32
2000-01-01 00:30:00,value13,value23,
2000-01-01 00:45:00,,value24,value34
2000-01-01 01:00:00,,,
2000-01-01 01:15:00,,,value36
2000-01-01 01:30:00,value17,,value37
2000-01-01 01:45:00,value18,,value38
2000-01-01 02:00:00,value19,,

注意 :

為此,文件必須在連接字段(本例中的標頭)上排序。 如果不是這種情況,可以使用sort命令。

我可以這樣做,但是它肯定不是運行速度最快的解決方案,但是對於您的數據,它返回正確的結果,並且代碼很短:

#!/bin/bash
CONTENT=$(cat template.scv)
for line in $CONTENT; do
    TMP=$(echo $line)
    for file in file1.csv file2.csv file3.csv; do
        RESULT=$(grep "^$line," $file | cut -d',' -f2)
        TMP=$(echo $TMP,$RESULT)
    done
    echo $TMP
done

輸出:

header,value1,value2,value3
1,,value21,
2,value12,value22,value32
3,value13,value23,
4,,value24,value34
5,,,
6,,,value36
7,value17,,value37
8,value18,,value38
9,value19,,

編輯:我的代碼缺少逗號( , ),所以對於較長的id來說,它不能正常工作編輯2:嗯,這不是“不是最快的解決方案”,它真的很慢

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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