CSV 파일을 분할하고 열을 기반으로 여러 CSV 파일을 만드는 방법

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...

파일에는 여러 날 동안의 분별 데이터가 포함되어 있으며 매분 업데이트됩니다. 다음과 같이 input.csv의 전날 TIMESTAMP 열을 기반으로 여러 csv 파일을 생성하는 bash 스크립트를 작성하고 싶습니다.

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 파일에 포함된 모든 데이터에 대한 파일을 생성하고 누락된 레코드를 고려하지 않습니다.

답변1

노력하다:

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

awkGNU 사용strftime()그리고MK타임()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): 형식타임스탬프사양에 따르면체재. 이것타임스탬프에포크 유형이어야 합니다.

타임스탬프의 형식을 지정해 보겠습니다.

$ 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

알아채다86400systime()현재 시간을 다음과 같이 반환한다고 말한 하루 또는 24시간의 초 수입니다.두번째~부터연대, 따라서 현재 시간에서 하루의 초를 빼면 어제 날짜의 시간을 얻습니다.

사람이 읽을 수 있는 형식으로 변환하고 그것이 무엇인지 살펴보겠습니다.

$ awk 'BEGIN{ print strftime("%Y %m %d 00 00 00", systime()-86400);  }'
2021 02 22 00 00 00

이제 타임스탬프가 무엇인지 명확합니다. 이 타임스탬프가 시작점으로 필요하고 start코드의 변수에 저장하기 때문에 시간/분/초를 "00"으로 사용합니다.

start그런 다음 for 루프를 사용하여 다음과 같이 변수의 타임스탬프에서 나머지 타임스탬프를 생성합니다 .

for(min=0; min<1440; min++)
    timestamp[strftime("%F %H:%M", mktime(start)+min*60)]

번호를 확인하세요 1440. 즉, 하루 또는 24시간의 분 수(24*60=1440)이지만 mktime()타임스탬프를 에포크 및 초 단위로 허용하므로 매 분에 60을 곱하여 초 단위의 타임스탬프를 얻은 다음 이 형식으로 변환합니다 %F %H:%M( F날짜는 %Y-%m-%d우리 H및 inute 와 동일한 ull 형식 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 
}

와 함께체계()mkdir디렉토리를 생성하기 위해 외부 명령을 호출하고 있습니다 .

다음 블록으로 이동하여 첫 번째 열에 타임스탬프가 표시되는 경우에만 다음 블록을 실행하세요.timestamp대량으로(substr($1,1,16) in timestamp) { "run these" };하위 문자열(문자열, 시작[, 길이])이 함수는길이- 문자 길이의 하위 문자열, 문자 수부터 시작시작.

  • cp=$1cp: 첫 번째 열을 변수 에 복사하고 그 값은 cp후속 처리에 사용됩니다.
  • gsub(/[-: ]|00$/, "", cp);; 변수에서 문자 -:공백 cp및 뒤에 오는 이중 0 "00"을 제거합니다.
  • print hdr ORS $0 >(yday"/"cp".csv");: var , a print에 저장하는 헤더 라인 (이것은 개행 문자입니다)hdrORS산소산출오른쪽에코코드에스기본적으로 구분 기호) 전체 줄을 $0관련 directory/fileName.csv.
  • close(yday"/"cp".csv");:폐쇄()작성한 후 파일입니다.
  • 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

답변2

기존 솔루션의 공백을 메우기 위한 무차별 접근 방식: 1440개의 파일을 테스트하지만 하루에 한 번만 테스트되며 그 날짜는 어제입니다. 큰 타격을 받은 경우:

(a) 어제 날짜를 두 가지 형식으로 가져옵니다: dt=202101032021-01-03.

(b) for루프를 실행합니다: for hm in {00..23}{00..59}.

${dt}${hm}00.csv(c) 존재하지 않는 파일 의 경우 : 필요한 헤더와 세부사항 라인을 인쇄하십시오.

귀하가 게시한 분할이 작동한다고 가정하고 타임스탬프의 공백을 채우면 됩니다.

이 Bash 솔루션은 테스트 및 검토를 거쳤습니다. 1초 이내에 실행됩니다.

#! /bin/bash

#.. In the current directory, this script makes dummy files such that:
#.. (a) There shall be a file for every minute in the previous day.
#..     with the name like YYYYmmddHHMM00 (e.g. 20210103173800.csv).
#.. (b) Any existing file with that name shall not be affected in any way.
#.. (c) Any new file shall contain a header line and one data line, like:
#..     TIMESTAMP,Data1,Data2,Data3,Data4
#..     "2021-01-03 17:38:00",0,0,0,0

#.. Create all the data apart from the hh,mm values.
fnYest="$( date -d '- 1 day' '+%Y%m%d%%s%%s00.csv' )"
dtYest="$( date -d '- 1 day' '+"%Y-%m-%d %%s:%%s:00"' )"
Header='TIMESTAMP,Data1,Data2,Data3,Data4'
Fields=',0,0,0,0'

#.. These five lines are debug, and can be removed.
printf 1>&2 'fnYest: %s\n' "${fnYest}"
printf 1>&2 'dtYest: %s\n' "${dtYest}"
printf 1>&2 "Filename:  ${fnYest}\n" 13 58
printf 1>&2 "TimeStamp: ${dtYest}\n" 13 58
printf 1>&2 "Line 1: %s\nLine 2: ${dtYest}%s\n" "${Header}" 13 58 "${Fields}"

#.. Creates the files that are missing.

for hh in {00..23}; do
    for mm in {00..59}; do
        printf -v fn "${fnYest}" "${hh}" "${mm}" 
        [[ -r "${fn}" ]] && continue
        printf > "${fn}" "%s\n${dtYest}%s\n" "${Header}" "${hh}" "${mm}" "${Fields}"
    done
done

관련 정보