열의 일부를 기준으로 CSV 파일 분할

열의 일부를 기준으로 CSV 파일 분할

file.csv다음과 같은 날짜와 시간이 포함된 CSV 파일이 있습니다 .

id0,2020-12-12T07:18:26,7f
id1,2017-04-28T19:59:00,80
id2,2017-04-28T03:14:35,e4
id3,2020-12-12T23:45:09,ff
id4,2020-12-12T09:12:34,a1
id5,2017-04-28T00:31:54,65
id6,2020-12-12T20:13:47,45
id7,2017-04-28T21:04:30,7f

2열의 날짜를 기준으로 파일을 분할하고 싶습니다. 위의 예를 사용하면 다음과 같은 2개의 파일이 생성됩니다.

file_1.csv
id1,2017-04-28T19:59:00,80
id2,2017-04-28T03:14:35,e4
id5,2017-04-28T00:31:54,65
id7,2017-04-28T21:04:30,7f

그리고

file_2.csv
id0,2020-12-12T07:18:26,7f
id3,2020-12-12T23:45:09,ff
id4,2020-12-12T09:12:34,a1
id6,2020-12-12T20:13:47,45

sort이 작업을 사용 하여 시도했지만 awk날짜와 시간을 기준으로 파일을 8개의 파일로 분할합니다.

sort -k2 -t, file.csv | awk -F, '!($2 in col) {col[$2]=++i} {print > ("file_" i ".csv")}'

날짜와 시간이 아닌 날짜만을 기준으로 파일을 분할하는 방법은 무엇입니까?

답변1

어때요?

awk -F', ' '
  { date = substr($2,1,10) }
  !(date in outfile) { outfile[date] = "file_" (++numout) ".csv" }
  { print > outfile[date] }
' file.csv

고유 날짜가 많은 대용량 파일인 경우 다음을 수행하여 "열린 파일이 너무 많습니다" 오류를 방지할 수 있습니다.

  { print >> outfile[date]; close(outfile[date]) }

답변2

$ cat tst.sh
#!/usr/bin/env bash

awk -F'[ -]' -v OFS='\t' '{print $2$3, NR, $0}' "${@:--}" |
sort -k1,1n -k2,2n |
cut -f3- |
awk -F'[ -]' '
    { curr = $2$3 }
    curr != prev {
        close(out)
        out = "file_" (++cnt) ".csv"
        prev = curr
    }
    { print > out }
'

./tst.sh file

$ head file_*
==> file_1.csv <==
id1, 2017-04-28T19:59:00, 80
id2, 2017-04-28T03:14:35, e4
id5, 2017-04-28T00:31:54, 65
id7, 2017-04-28T21:04:30, 7f

==> file_2.csv <==
id0, 2020-12-12T07:18:26, 7f
id3, 2020-12-12T23:45:09, ff
id4, 2020-12-12T09:12:34, a1
id6, 2020-12-12T20:13:47, 45

위의 내용은 POSIX awk, sort 및 cut에서 강력하고 효율적이며 이식 가능하게 작동하며 출력 파일의 입력 순서를 유지합니다.

처음 3단계에서 입력 파일 내용을 다시 정렬하는 방법은 다음과 같습니다.

$ cat file
id0, 2020-12-12T07:18:26, 7f
id1, 2017-04-28T19:59:00, 80
id2, 2017-04-28T03:14:35, e4
id3, 2020-12-12T23:45:09, ff
id4, 2020-12-12T09:12:34, a1
id5, 2017-04-28T00:31:54, 65
id6, 2020-12-12T20:13:47, 45
id7, 2017-04-28T21:04:30, 7f

이렇게 하면 최종 awk 스크립트가 실행될 때 행이 $2부터 시작하여 연도+월별로 정렬되어 날짜+시간이 동일한 모든 행의 입력 순서가 유지됩니다.

$ awk -F'[ -]' -v OFS='\t' '{print $2$3, NR, $0}' file
202012  1       id0, 2020-12-12T07:18:26, 7f
201704  2       id1, 2017-04-28T19:59:00, 80
201704  3       id2, 2017-04-28T03:14:35, e4
202012  4       id3, 2020-12-12T23:45:09, ff
202012  5       id4, 2020-12-12T09:12:34, a1
201704  6       id5, 2017-04-28T00:31:54, 65
202012  7       id6, 2020-12-12T20:13:47, 45
201704  8       id7, 2017-04-28T21:04:30, 7f

$ awk -F'[ -]' -v OFS='\t' '{print $2$3, NR, $0}' file | sort -k1,1n -k2,2n
201704  2       id1, 2017-04-28T19:59:00, 80
201704  3       id2, 2017-04-28T03:14:35, e4
201704  6       id5, 2017-04-28T00:31:54, 65
201704  8       id7, 2017-04-28T21:04:30, 7f
202012  1       id0, 2020-12-12T07:18:26, 7f
202012  4       id3, 2020-12-12T23:45:09, ff
202012  5       id4, 2020-12-12T09:12:34, a1
202012  7       id6, 2020-12-12T20:13:47, 45

$ awk -F'[ -]' -v OFS='\t' '{print $2$3, NR, $0}' file | sort -k1,1n -k2,2n | cut -f3-
id1, 2017-04-28T19:59:00, 80
id2, 2017-04-28T03:14:35, e4
id5, 2017-04-28T00:31:54, 65
id7, 2017-04-28T21:04:30, 7f
id0, 2020-12-12T07:18:26, 7f
id3, 2020-12-12T23:45:09, ff
id4, 2020-12-12T09:12:34, a1
id6, 2020-12-12T20:13:47, 45

답변3

지금 하고 있는 방식대로 수행한다는 것은 먼저 실행 sort한 다음 다른 파일로 분할하고 awk배열 사용을 피하는 것을 의미합니다.

<infile sort -t, -k2 \
|awk -F, '{
     substr($2,1,10)!=prev && nxt++;
     print >>("file_"nxt".csv"); close("file_"nxt".csv");
     prev=substr($2,1,10);
}'

관련 정보