스크립트를 사용하여 날짜 범위를 일 단위로 분할하는 방법

스크립트를 사용하여 날짜 범위를 일 단위로 분할하는 방법

다음과 같은 입력이 있습니다.

      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)는 각 라인에 복제됩니다. 

  1. 실제로 입력 레코드는 하이브 테이블에서 나오며 출력 레코드도 이를 분할 테이블에 저장합니다.

수정 사항:

날짜 분할은 괜찮습니다. 분할 날짜에 따라 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제거 하십시오 .dodone
  • 시작 날짜, 시작 시간, 종료 날짜, 종료 시간 및 기타 데이터를 읽습니다.  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_seq0으로 설정하면 루프가 종료됩니다.
  • "기타 데이터"(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_dataval1 앞에 공백이 포함됩니다. 그런 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

관련 정보