다음과 같은 입력이 있습니다.
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
행 중 하나는 여러 날에 걸친 날짜 범위를 지정하고, 병렬 처리(여러 날) 범위를 용이하게 하기 위해 범위를 별도의 기간으로 분할하고 싶습니다. 각 기간은 하루의 하위 집합(별도의 줄에 있는 각 기간)입니다.
출력은
2015-10-13 07:00:02 2015-10-13 23:59:59 45 1900
2015-10-14 00:00:01 2015-10-14 23:59:59 45 1900
2015-10-15 00:00:01 2015-10-15 23:59:59 45 1900
2015-10-16 00:00:01 2015-10-16 23:59:59 45 1900
2015-10-17 00:00:01 2015-10-17 23:59:59 45 1900
2015-10-18 00:00:01 2015-10-18 23:59:59 45 1900
2015-10-19 00:00:01 2015-10-19 00:00:00 45 1900
종료 시간 이후의 데이터(val1 및 val2)가 각 행에 복사됩니다.
- 실제로 입력 레코드는 하이브 테이블에서 나오고 출력 레코드도 이를 분할 테이블에 저장합니다.
개정하다:
날짜 분할은 괜찮습니다. 또한 분할 날짜를 기준으로 val2 값을 분할해야 합니다.
날짜 차이가 2이면 2개의 행을 분할합니다.
- 라인 1:
비율 = 첫날에 소비한 시간의 비율(즉, 첫날의 끝 - 시작)/값 1
값2=비율*값2
- 2호선:
비율 = 첫날에 소비한 시간의 비율(즉, 둘째 날의 끝~시작)/값 1
값 2= 비율*값2
이 스크립트를 어떻게 작성해야 하나요?
답변1
이 스크립트는 귀하가 원하는 작업을 수행합니다(귀하의 요구 사항을 올바르게 이해한 경우). 입력에 하나의 헤더 행이 있고 날짜/시간 범위가 있는 행이 여러 개 있을 수 있도록 사양을 자유롭게 추정할 수 있습니다. 이에 대해서는 아래에서 설명하고 더 자세히 논의하겠습니다.
#!/bin/sh
if IFS= read header
then
printf "%s\n" "$header"
else
echo 'EOF on first line!' >&2
exit 1
fi
while read start_date start_time end_date end_time other_data # See note, below.
do
start_epoch=$(date +"%s" -d "$start_date $start_time") || {
echo "Error processing start date&time $start_date $start_time" >&2
exit 1
}
end_epoch=$(date +"%s" -d "$end_date $end_time") || {
echo "Error processing end date&time $end_date $end_time" >&2
exit 1
}
if [ "$end_epoch" -lt "$start_epoch" ]
then
echo "End date&time $end_date $end_time is before start date&time $start_date $start_time" >&2
# Now what?
continue
fi
ok_seq=1 # Flag: we are moving forward.
current_date="$start_date"
current_time="$start_time"
while [ "$ok_seq" -ne 0 ]
do
# Most days end at 23:59:59.
eod_time="23:59:59"
eod_epoch=$(date +"%s" -d "$current_date $eod_time") || {
# This should never happen.
echo "Error processing end-of-day date&time $current_date $eod_time" >&2
exit 1
}
if [ "$end_epoch" -lt "$eod_epoch" ] # We’re passing the end of the date/time range.
then
if [ "$current_date" != "$end_date" ]
then
# Sanity check -- this should not happen.
echo "We're finishing, but the current date is $current_date and the end date is $end_date" >&2
fi
eod_time="$end_time"
ok_seq=0
fi
# See note, below.
printf "%s %s %s %s %s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"
# We could also use +"%F" for the full YYYY-mm-dd date.
current_date=$(date +"%Y-%m-%d" -d "$current_date next day") || {
# This shouldn’t happen.
echo "Error getting next day after $current_date" >&2
exit 1
}
current_time="00:00:01"
done
done
논의하다:
- 제목 줄을 읽어보세요. 실패하면 스크립트를 중단합니다. 성공하면 해당 행이 출력에 기록됩니다. 귀하의 질문에서 알 수 있듯이 헤더가 출력에 포함되는 것을 원하지 않는 경우 해당
printf "%s\n" "$header"
문을 제거하십시오. - 위와 같이: 루프, 입력 끝에 도달할 때까지(또는 치명적인 오류가 발생할 때까지) 입력에서 시작/끝/값 줄을 읽습니다. 이 작업을 원하지 않으면 삭제
while
하고do
그에 따라 을 삭제하십시오done
. - 시작 날짜, 시작 시간, 종료 날짜, 종료 시간 및 기타 데이터를 읽습니다.
other_data
종료 시간 이후의 모든 내용, 즉 val1 및 val2(및 그 사이의 모든 공백)를 포함합니다. - 이 명령을 사용하면 날짜/시간 문자열을 Unix "epoch 시간"(1970-01-01 00:00:00(GMT) 이후의 초 수)으로 변환할 수 있습니다. 이를 통해 입력의 유효성을 검사하고(오류 발생 시 종료) 비교할 수 있는 숫자도 제공됩니다. (하지만 YYYY-MM-DD HH:MM:SS 형식의 값에 대해 문자열 비교를 수행할 수 있다고 가정합니다.)
date +"%s" -d "date/time string"
- 종료 날짜/시간이 시작 날짜/시간보다 이전인 경우 이 레코드를 건너뛰고 다음 행으로 이동합니다. 이 경우 다른 작업(예: 종료)을 수행하려면 이 코드를 변경하세요.
ok_seq
일일 루프를 제어하는 데 사용할 플래그( )를 설정합니다 . 첫날의 시작 날짜/시간을 전체 기간의 시작 날짜/시간으로 초기화합니다.- 각 출력 행에서 시작 날짜와 종료 날짜는 동일합니다. 대부분의 행에서 하루 종료(eod) 시간은 23:59:59입니다. (동일 날짜) + 23:59:59가 종료 날짜/시간보다 크면(나중에) 범위의 마지막 날(출력 행)에 있습니다. eod 시간을 종료 시간으로 설정하고
ok_seq
루프를 종료할 수 있도록 0으로 설정합니다. - "기타 데이터"(val1 및 val2 등)를 포함하여 출력 라인을 작성합니다.
- 다음 날의 날짜를 계산합니다. 시작 시간을 00:00:01로 설정하면 첫 번째 줄을 제외한 모든 출력 줄에 표시됩니다.
예:
$ cat input
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
2015-11-01 08:30:00 2015-11-05 15:00:00 42 6083
2015-12-27 12:00:00 2016-01-04 12:34:56 17 quux
$ ./script < input
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-13 23:59:59 45 1900
2015-10-14 00:00:01 2015-10-14 23:59:59 45 1900
2015-10-15 00:00:01 2015-10-15 23:59:59 45 1900
2015-10-16 00:00:01 2015-10-16 23:59:59 45 1900
2015-10-17 00:00:01 2015-10-17 23:59:59 45 1900
2015-10-18 00:00:01 2015-10-18 23:59:59 45 1900
2015-10-19 00:00:01 2015-10-19 00:00:00 45 1900
2015-11-01 08:30:00 2015-11-01 23:59:59 42 6083
2015-11-02 00:00:01 2015-11-02 23:59:59 42 6083
2015-11-03 00:00:01 2015-11-03 23:59:59 42 6083
2015-11-04 00:00:01 2015-11-04 23:59:59 42 6083
2015-11-05 00:00:01 2015-11-05 15:00:00 42 6083
2015-12-27 12:00:00 2015-12-27 23:59:59 17 quux
2015-12-28 00:00:01 2015-12-28 23:59:59 17 quux
2015-12-29 00:00:01 2015-12-29 23:59:59 17 quux
2015-12-30 00:00:01 2015-12-30 23:59:59 17 quux
2015-12-31 00:00:01 2015-12-31 23:59:59 17 quux
2016-01-01 00:00:01 2016-01-01 23:59:59 17 quux
2016-01-02 00:00:01 2016-01-02 23:59:59 17 quux
2016-01-03 00:00:01 2016-01-03 23:59:59 17 quux
2016-01-04 00:00:01 2016-01-04 12:34:56 17 quux
한 달에서 다음 달로 이동하는 것뿐만 아니라 1년에서 다음 해로 이동하는 것도 문제가 없습니다.
노트: 위 버전의 스크립트를 작성했을 때 종료 시간과 val1 사이의 공백을 캡처하는 방법을 몰랐기 때문에 얻은 출력은 다음과 같습니다.
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-13 23:59:59 45 1900
2015-10-14 00:00:01 2015-10-14 23:59:59 45 1900
2015-10-15 00:00:01 2015-10-15 23:59:59 45 1900
︙
그래서 나는 "속임수"를 사용하여 명령에 "적절한 양"의 공간을 추가했습니다 printf
(마지막 명령 이전에 %s
). 그러나 입력의 간격을 변경하면 위 스크립트 버전은 다시 잘못 정렬된 열을 생성합니다. 조금 지저분하긴 하지만 어떻게 해결해야 할지 알아냈습니다. while …
do
... 줄을 다음으로 바꾸세요 start_epoch=…
.
while read start_date start_time end_date other_data
do
# $other_data includes end_time and all the following values.
# Break them apart:
end_time="${other_data%%[ ]*}"
other_data="${other_data#"$end_time"}"
start_epoch=…
명령에서 제거 end_time
된 위치 에서 대괄호와 사이의 문자는 공백과 탭입니다. 이제 val1 앞의 공간이 포함됩니다. 그런 다음 다음 으로 변경하십시오 .read
[
]
other_data
printf
printf "%s %s %s %s%s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"
(참고하세요.아니요네 번째와 다섯 번째 사이의 공간 %s
). 이제 끝났습니다.
답변2
나는 당신이 상단 헤더 행을 제거하려고한다고 생각합니다. 입력을 받는 함수가 "timefunc"이라고 가정해 보겠습니다. 다음과 같이 cut 명령에서 timefunc의 출력을 파이핑해 볼 수도 있습니다.
timefunc | cut -d$'\n' -f2
이제 출력은 다음과 같습니다.
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
답변3
grep을 사용하여 출력에서 헤더 줄을 제거할 수 있습니다.
inputcmd | grep -v startdate