如何拆分 CSV 文件並基於列創建多個 CSV 文件
我有一個格式為 csv 的文件:
輸入.csv:
TIMESTAMP,Data1,Data2,Data3,Data4 "2021-01-03 00:00:00",80953,3.243183,2.943338,358.0123 "2021-01-03 00:01:00",80954,2.173187,1.990327,344.5851 ... "2021-01-03 23:59:00",80957,4.04172,3.82053,355.5481 "2021-01-04 00:00:00",80955,3.700353,3.593842,346.2665 ... "2021-01-04 23:59:00",80956,3.125094,2.922542,350.9915 "2021-01-05 00:00:00",80957,4.04172,3.82053,355.5481 ... "2021-01-05 23:59:00",80956,3.125094,2.922542,350.9915 etc...
該文件包含多天的每分鐘數據,並且每分鐘更新一次。我想編寫一個 bash 腳本,該腳本根據 input.csv 中前一天的 TIMESTAMP 列創建多個 csv 文件,如下所示:
cat 20210103000000.csv TIMESTAMP,Data1,Data2,Data3,Data4 "2021-01-03 00:00:00",80953,3.243183,2.943338,358.0123
cat 20210103000100.csv TIMESTAMP,Data1,Data2,Data3,Data4 "2021-01-03 00:01:00",80954,2.173187,1.990327,344.5851
……以此類推,直到那天的最後一分鐘
cat 20210103235900.csv TIMESTAMP,Data1,Data2,Data3,Data4 "2021-01-03 23:59:00",80957,4.04172,3.82053,355.5481
如果某個時間的數據失去/不存在,例如“2021-01-03 17:06:00”,則必須創建以下文件:
20210103170600.csv: TIMESTAMP,Data1,Data2,Data3,Data4 "2021-01-03 17:06:00",0,0,0,0
本文的解決方案如何拆分每個初始列(帶標題)的 CSV 文件?
awk -F ',' 'NR==1{h=$0; next};!seen[$1]++{f=$1".csv"; print h > f};{f=$1".csv"; print >> f; close(f)}' input.csv
部分解決了我的問題,但它為 input.csv 文件中包含的所有數據創建文件,並且不考慮失去的記錄。
嘗試:
awk -F, -v yesterday="$(date -d'-1day' +'%F')" ' BEGIN{ for(min=0; min<1440; min++){ mins = "date +%F\" " "\"%T -d\"" min "minutes" yesterday"\"" mins |getline yday_tmp; close(mins); timestamp["\"" yday_tmp "\""] } } NR==1{ hdr=$0; next } ($1 in timestamp){ cp=$1; gsub(/[-": ]/, "", cp); print hdr ORS $0 >(cp".csv"); close(cp".csv"); delete timestamp[$1] } END{ for (x in timestamp){ cpx=x; gsub(/[-": ]/, "", cpx); print hdr ORS x ",0,0,0,0" >(cpx".csv") close(cpx".csv") } }' infile
awk
對strftime()和mktime()函式使用 GNU來減少生成時間戳的執行時間,而不是呼叫外部date
命令,並將文件儲存在單獨的日期目錄中並刪除所有雙引號:gawk -F, ' BEGIN{ start=strftime("%Y %m %d 00 00 00", systime()-86400); for(min=0; min<1440; min++) timestamp[strftime("%F %H:%M", mktime(start)+min*60)] } { gsub(/"/,"") } NR==1{ hdr=$0; yday=strftime("dir_%Y%m%d", systime()-86400); system("mkdir "yday); next } (substr($1,1,16) in timestamp){ cp=$1; gsub(/[-: ]|00$/, "", cp); print hdr ORS $0 >(yday"/"cp".csv"); close(yday"/"cp".csv"); delete timestamp[substr($1,1,16)] } END{ for (x in timestamp){ cpx=x; gsub(/[-: ]/, "", cpx); print hdr ORS x ",0,0,0,0" >(yday"/"cpx".csv"); close(yday"/"cpx".csv") } }' infile
如在GNU
awk
文件中:
systime()
返回目前時間作為自紀元以來的秒數(在 POSIX 系統上為 1970-01-01 00:00:00 UTC)。讓我們列印它:$ awk 'BEGIN{ print systime() }' 1614100199
mktime(timestamp)
將時間戳格式*YYYY MM DD HH MM SS
*轉換為紀元時間。讓我們列印它;
$ awk 'BEGIN{ print mktime("2021 02 22 00 00 00") }' 1613939400
strftime(format, timestamp)
:根據格式中的規範格式化**時間戳。時間戳應該是 epoch 類型。讓我們格式化一個時間戳:
$ awk 'BEGIN{ print strftime("%Y-%m-%d %H:%M:%S", mktime("2021 02 23 01 02 00")) }' 2021-02-23 01:02:00
記住上述所有 3 個
awk
時間函式。現在讓我們看看他們在答案中使用了什麼:
$ awk 'BEGIN{ print systime()-86400 }' 1614014848
注意*
86400
*是每天或 24 小時的秒數;在上面我們說systime()
返回目前時間作為自Epoch以來的秒數,所以如果我們從目前時間減去一天中的秒數,它會給我們昨天日期的時間。讓我們將其轉換為人類可讀的,看看那是什麼:
$ awk 'BEGIN{ print strftime("%Y %m %d 00 00 00", systime()-86400); }' 2021 02 22 00 00 00
現在很清楚它是什麼時間戳,我們使用 Hour/Min/Sec 為“00”,因為我們需要這個時間戳作為起點,並將其儲存到
start
程式碼中的變數中。然後我們使用 for 循環從變數中的時間戳生成其餘時間戳,
start
如下所示:for(min=0; min<1440; min++) timestamp[strftime("%F %H:%M", mktime(start)+min*60)]
注意數字
1440
?即一天或 24 小時中的分鐘數 (2460=1440);但是mktime()
接受時間戳作為紀元和秒,所以我們將每分鐘乘以 60 以獲得以秒為單位的時間戳,然後將其轉換為這種格式%F %H:%M
(F
日期的 ull 格式與我們的和inute 相同%Y-%m-%d
)並保存到我們命名的awk*數組中;現在我們每分鐘都有所有時間戳的昨天日期。H``M``timestamp[...]
您甚至可以列印它們以查看它們是什麼:
$ awk ' BEGIN{ start=strftime("%Y %m %d 00 00 00", systime()-86400); for(min=0; min<1440; min++) timestamp[strftime("%F %H:%M", mktime(start)+min*60)]; for (t in timestamp) print t }'
下面的gsub()函式從目前行刪除所有引號:
{ gsub(/"/,"") }
然後我們將輸入文件的第一行(即標題行)備份到
hdr
變數中,因為我們需要將標題行添加到我們生成的每個文件中;然後我們也創建一個帶有昨天日期的目錄,它的格式為dir_%Y%m%d
; 下面的程式碼塊僅在第一行輸入時執行一次NR==1 { "run these" }
:NR==1{ hdr=$0; yday=strftime("dir_%Y%m%d", systime()-86400); system("mkdir "yday); next }
使用system()函式,我們呼叫外部命令
mkdir
來創建該目錄。進入下一個塊,只有在*
timestamp
數組中看到第一列的時間戳時才執行下一個塊(substr($1,1,16) in timestamp) { "run these" }
;substr(字元串,開始$$ , length $$)函式返回 string 的長度*-character-long 子字元串,從字元號start 開始。
cp=$1
:我們將第一列複製到cp
變數中,我們將在cp
後面的處理中使用值。gsub(/[-: ]|00$/, "", cp);
; 從變數中去除字元-
和空格以及尾隨的雙零“00”。:``cp
print hdr ORS $0 >(yday"/"cp".csv");
:hdr
var 中的標題行,an (預設情況下,它是O utput Record SORS
分隔符的換行符)和整行到相關的.$0``directory/fileName.csv
close(yday"/"cp".csv");
: close()寫入後的文件。delete timestamp[substr($1,1,16)]
: 並從數組中刪除該時間戳。在
END { "run these" }
塊中,我們將輸入文件中不存在的時間戳列印到文件中。處理多個文件並將每個輸入文件拆分為單獨的日目錄。
gawk -F, ' { gsub(/"/,"") } FNR==1{ delete timestamp; start=strftime("%Y %m %d 00 00 00", systime()-86400); for(min=0; min<1440; min++) timestamp[strftime("%F %H:%M", mktime(start)+min*60)] hdr=$0; yday=strftime("%Y%m%d", systime()-86400); fname=FILENAME; sub(/\.csv$/,"", fname); dirName=fname"_"yday; system("mkdir "dirName); next } (substr($1,1,16) in timestamp){ cp=$1; gsub(/[-: ]|00$/, "", cp); print hdr ORS $0 >(dirName"/"cp".csv"); close(dirName"/"cp".csv"); delete timestamp[substr($1,1,16)] } ENDFILE{ for (x in timestamp){ cpx=x; gsub(/[-: ]/, "", cpx); print hdr ORS x ",0,0,0,0" >(dirName"/"cpx".csv"); close(dirName"/"cpx".csv") } }' multiple*.csv