UTC가 포함된 ISO-8601 CSV 파일을 사용하여 정의된 시간 오프셋/중단/차이(예: 2시간) 에 따라 밀리초 정밀도( +%FT%T.%3NZ
예:) 로 분할하는 간단한 방법(예: 라이너)이 있는지 궁금합니다. 2021-05-27T13:59:33.641Z
타임스탬프.
항상 그렇듯이, 이를 얻는 방법에는 여러 가지가 있으며 비슷한 질문을 가진 다른 사용자의 경우 다른 옵션도 포괄적인 답변과 관련이 있을 수 있습니다.
GNU Bash 4.4.23
...git 2.31.1의 ,GNU sed 4.8
,GNU Awk 5.0.0
(및 함께 번들로 제공되는 다른 모든 도구)를 사용/가집니다 .xsv 0.13.0
jq 1.6
윈도우 7 의 경우- ...대화형 셸의 스크립트에서 사용하는 것을 선호합니다.
- ...세미콜론(
;
)을 구분 기호로 사용하고 쉼표는 사용하지 마세요. - ... 하다아니요내 값 인용(예: 작은따옴표(
'
) 또는 큰따옴표( )로"
묶음 ) - ...제목 없음
- ...전체 CSV를 변수에 저장했으며 결과를 추가로 분석할 수 있도록 결과를 변수(배열?)에 저장하고 싶습니다.
- 내 칼럼은아니요실제로는 길이가 고정되어 있으며 영숫자 외에 공백과 하이픈이 포함될 수 있습니다.
- 타임스탬프는 실제 데이터의 8개 열 중 5번째입니다.
- 파일이 최대 250,000줄, 20MiB라고 가정할 수 있습니다.
- 스크립트/명령이 0.5초 미만으로 소요된다면 i5-4300U에서 더 좋지만, 최대 5~10초는 여전히 거래 중단 시간이 아닙니다.
예
분할을 위한 오프셋이 있고 2 hours
아무것도 난독화하지 않는 경우 이 파일은 다음과 같습니다.
abc;square;2021-05-27T14:15:39.315Z
def;circle;2021-05-27T14:17:03.416Z
ghi;triang;2021-05-27T14:45:13.520Z
abc;circle;2021-05-27T15:25:47.624Z
ghi;square;2021-05-27T17:59:33.641Z
def;triang;2021-05-27T18:15:33.315Z
abc;circle;2021-05-27T21:12:13.350Z
ghi;triang;2021-05-27T21:15:31.135Z
다음 세 부분으로 나누어집니다
abc;square;2021-05-27T14:15:39.315Z
def;circle;2021-05-27T14:17:03.416Z
ghi;triang;2021-05-27T14:45:13.520Z
abc;circle;2021-05-27T15:25:47.624Z
ghi;square;2021-05-27T17:59:33.641Z
def;triang;2021-05-27T18:15:33.315Z
abc;circle;2021-05-27T21:12:13.350Z
ghi;triang;2021-05-27T21:15:31.135Z
면책조항: 저는 원어민이 아니므로 다른 말로 바꿔서 이 질문을 더 쉽게 이해할 수 있다면 계속 진행하시기 바랍니다. 긴 답장. 예를 들어, 내 사용 사례에 적용되지 않는 옵션(쉼표, 따옴표)을 지정하거나 semicolon
이 질문의 텍스트에 단어와 기호를 모두 사용하는 것도 SEO 목적입니다.;
답변1
변수의 예제 CSV 데이터를 고려하면 다음과 같습니다 $csv
.
gawk '
function timestamp2epoch(ts, m) {
if(match(ts, /([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\..*/, m))
return mktime(m[1] " " m[2] " " m[3] " " m[4] " " m[5] " " m[6])
else
return -1
}
BEGIN {
FS = ";"
interval = 2 * 3600 # 2 hours
}
{ t = timestamp2epoch($3) }
t > start + interval { start = t; n++ }
{ batch[n] = batch[n] (batch[n] == "" ? "" : "/") $0 }
END {
PROCINFO["sorted_in"] = "@ind_num_asc"
for (i in batch)
print batch[i]
}
' <<<"$csv"
산출
abc;square;2021-05-27T14:15:39.315Z/def;circle;2021-05-27T14:17:03.416Z/ghi;triang;2021-05-27T14:45:13.520Z/abc;circle;2021-05-27T15:25:47.624Z
ghi;square;2021-05-27T17:59:33.641Z/def;triang;2021-05-27T18:15:33.315Z
abc;circle;2021-05-27T21:12:13.350Z/ghi;triang;2021-05-27T21:15:31.135Z
이는 쉘 배열로 읽을 수 있습니다. 예를 들면 다음과 같습니다.
mapfile -t batches < <(gawk '...' <<<"$csv")
declare -p batches
declare -a batches=([0]="abc;square;2021-05-27T14:15:39.315Z/def;circle;2021-05-27T14:17:03.416Z/ghi;triang;2021-05-27T14:45:13.520Z/abc;circle;2021-05-27T15:25:47.624Z" [1]="ghi;square;2021-05-27T17:59:33.641Z/def;triang;2021-05-27T18:15:33.315Z" [2]="abc;circle;2021-05-27T21:12:13.350Z/ghi;triang;2021-05-27T21:15:31.135Z")
그런 다음 다음과 같이 상호 작용하십시오.
for ((i = 0; i < "${#batches[@]}"; i++)); do
IFS="/" read -ra records <<<"${batches[i]}"
echo "batch $i"
for record in "${records[@]}"; do echo " $record"; done
echo
done
batch 0
abc;square;2021-05-27T14:15:39.315Z
def;circle;2021-05-27T14:17:03.416Z
ghi;triang;2021-05-27T14:45:13.520Z
abc;circle;2021-05-27T15:25:47.624Z
batch 1
ghi;square;2021-05-27T17:59:33.641Z
def;triang;2021-05-27T18:15:33.315Z
batch 2
abc;circle;2021-05-27T21:12:13.350Z
ghi;triang;2021-05-27T21:15:31.135Z
답변2
다음 Perl 스크립트는 입력 파일을 출력하고 이전 시작 기간의 2시간 이내에 없는 줄이 나타날 때마다 빈 줄을 추가합니다. 입력을 최대 지속 시간이 2시간인 배치로 분할합니다.
시작 기간은 첫 번째 행을 읽을 때 설정되고 추가 빈 행이 인쇄될 때만 업데이트됩니다. 이는 최소한 2시간마다 새 배치가 생성되도록 하기 위한 것입니다. 그렇지 않으면 샘플 입력이 두 개의 배치로만 분할됩니다. 시간(14:15~18:15에 6줄, 21:12와 21:15에 2줄), 16:45에 추가 로그 항목을 추가하고 20:00 항목에 또 다른 로그 항목을 추가하여 샘플 입력.
입력의 세 번째 필드에서 날짜와 시간을 가져옵니다. Perl 배열은 $F[2]
배열의 세 번째 필드와 마찬가지로 1이 아닌 0에서 시작합니다 @F
.
#!/usr/bin/perl
use strict;
use Date::Parse;
my $start;
while(<>) {
chomp;
my $approx;
my @F = split /;/;
# approximate date/time to start of hour
($approx = $F[2]) =~ s/:\d\d:\d\d\.\d+Z$/:00:00/;
my $now = str2time($approx);
$start = $now if ($. == 1);
if (($now - $start) > 7200) {
$start = $now;
print "\n";
};
print "$_\n";
}
예제 출력:
$ ./split.pl input.csv
abc;square;2021-05-27T14:15:39.315Z
def;circle;2021-05-27T14:17:03.416Z
ghi;triang;2021-05-27T14:45:13.520Z
abc;circle;2021-05-27T15:25:47.624Z
ghi;square;2021-05-27T17:59:33.641Z
def;triang;2021-05-27T18:15:33.315Z
abc;circle;2021-05-27T21:12:13.350Z
ghi;triang;2021-05-27T21:15:31.135Z
별도의 파일로 출력이 필요한 경우 다음을 수행할 수 있습니다.
#!/usr/bin/perl
use strict;
use Date::Parse;
my $start;
# output-file counter
my $fc = 1;
my $outfile = "file.$fc.csv";
open (my $fh, ">", $outfile) || die "couldn't open $outfile for write: $!\n";
while(<>) {
chomp;
my $approx;
my @F = split /;/;
# approximate date/time to start of hour
($approx = $F[2]) =~ s/:\d\d:\d\d\.\d+Z$/:00:00/;
my $now = str2time($approx);
$start = $now if ($. == 1);
if (($now - $start) > 7200) {
$start = $now;
close($fh);
$fc++;
$outfile = "file.$fc.csv";
open ($fh, ">", $outfile) || die "couldn't open $outfile for write: $!\n";
};
print $fh "$_\n";
}
스크립트의 두 버전 중 하나를 처리할 수 있는 시간 형식에서 더 유연하게 만들려면 다음을 사용하세요.
($approx = $F[2]) =~ s/:\d\d:\d\d(?:\.\d+)?Z?$/:00:00/;
이를 통해 시간 문자열의 소수 부분과 Z를 모두 선택적으로 사용할 수 있습니다.
답변3
gensub()
다음에 는 GNU awk를 사용하세요 mktime()
.
$ cat tst.awk
BEGIN {
FS = ";"
maxSecs = 2 * 60 * 60
prevTime = -(maxSecs + 1)
}
{
split($3,dt,/[.]/)
dateHMS = gensub(/[-T:]/," ","g",dt[1])
currSecs = mktime(dateHMS,1) "." dt[2]
secsDelta = currTime - prevTime
prevTime = currTime
}
secsDelta > maxSecs {
close(out)
out = "out" (++numOut)
}
{ print > out }
$ awk -f tst.awk file
$ head out?
==> out1 <==
abc;square;2021-05-27T14:15:39.315Z
def;circle;2021-05-27T14:17:03.416Z
ghi;triang;2021-05-27T14:45:13.520Z
abc;circle;2021-05-27T15:25:47.624Z
==> out2 <==
ghi;square;2021-05-27T17:59:33.641Z
def;triang;2021-05-27T18:15:33.315Z
==> out3 <==
abc;circle;2021-05-27T21:12:13.350Z
ghi;triang;2021-05-27T21:15:31.135Z
답변4
파일의 모든 날짜가 같은 날에 속하는 경우:
#!/usr/bin/awk -f
BEGIN {
FS=OFS=";"
ho = 1
}
{
# Split the last field in date and times
split($NF, a, "T")
# Get the hour from time
h = a[2]
sub(/:.*$/, "", h)
if (lh == 0) lh = h+ho
if (h > lh) {
lh = h+ho
print "\n"
}
}1
스크립트 블록 ho
에서 (시간 오프셋)을 편집 BEGIN
하여 csv에서 다른 시간 오프셋으로 분할할 수 있습니다.
#!/usr/bin/awk -f
BEGIN {
FS=OFS=";"
# Set here the hour offset
hour_offset = 1
# Get the hour values in seconds
ho = 60 * 60 * hour_offset
}
{
sub(/Z$/, "", $NF)
# Call /bin/date and translate the 'visual date' to
# epoch timestamp.
cmd="/bin/date -d " $NF " +%s"
epoch=((cmd | getline line) > 0 ? line : -1)
close(cmd)
if (epoch == -1) {
print "Date throw an error at : " NR;
exit 1;
}
# If the lh (last hour) is not set, set it
# to the current value for the epoch time plus
# the chosen offset
if (!lh) lh = epoch + ho
# if the current offset less the the old hour processed is
# greater then the offset you choose: update the offset and
# print the separator
if (epoch - lh > ho) {
lh = epoch + ho
print ""
}
}1