
다음과 같은 입력이 있습니다.
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:
ratio= 첫째 날에 보낸 시간 비율(즉, 첫째 날 종료-시작) /발1
값2= 비율*값2
- 행 2:
ratio= 첫째 날에 보낸 시간 비율(즉, 둘째 날 종료-시작) /발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
한 달에서 다음 달로뿐만 아니라 한 해에서 다음 해로 롤오버하는 데 문제가 없다는 점을 관찰하세요.
메모: 위 버전의 스크립트를 작성할 때 종료 시간과 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
에서 제거되었으며 read
대괄호 [
와 사이의 문자는 ]
공백과 탭입니다. 이제 other_data
val1 앞에 공백이 포함됩니다. 그런 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