특정 날짜 범위 내에서 매월 함수 실행

특정 날짜 범위 내에서 매월 함수 실행

문제: 최소 및 최대 날짜에 대한 매개변수(예: script.sh 20190801, 20201005)를 사용하여 해당 날짜 범위의 매월 함수를 실행합니다. (예: 20190801-20190831, 20190901-20190931...)

뒷이야기: 날짜 범위 내의 데이터를 기반으로 CSV를 생성하는 기능이 있는데 더 큰 날짜 범위에 대해 실행하는 데 시간이 너무 오래 걸립니다. 이를 염두에 두고 매월 실행한 다음 CSV를 다시 연결해 보았습니다. Python이나 Javascript에서 비슷한 것을 시도하고 날짜/시간 라이브러리를 사용하겠지만 일부 회사 제한 사항에서는 bash/shell을 독점적으로 사용해야 합니다.

현재 생각하고 있는 것은 다음과 같지만 대부분 실패합니다.

예를 들어 20190101 20201231을 아래 코드에 전달하여 예상되는 결과를 얻었지만 20190105 20200820과 같은 것을 지원하도록 수정하는 방법을 잘 모르겠습니다.

DateMin=$1
DateMax=$2

if [ $DateMin -ge $DateMax ]; then
    echo "ERROR: Minimum Date Cannot be above Maximum Date"
    exit
fi

let DateDelta=DateMax-DateMin

for YearDelta in `seq $DateDelta -10000 0`
do
    let TempDelta=$YearDelta-1130
    for MonthDelta in `seq $YearDelta -100 $TempDelta`
    do
        let TempMin=$DateMax-$MonthDelta
        let TempMax=$DateMax-$MonthDelta+30
        call_to_function $TempMin $TempMax
    done
done

더 쉽게 볼 수 있도록 여기에 repl.it을 만들었습니다.https://repl.it/repls/CanineAdmirableSeahorse#main.sh "bash main.sh 20190101 20201231"을 실행하면 예상되는 출력입니다.

답변1

이 프로그램을 사용하는 것은 어떻습니까 date? 이 프로그램은 날짜와 함께 많은 것을 계산할 수 있습니다. 예는 다음과 같습니다.

DateMinStr="${1:0:4}-${1:4:2}-01"
DateMaxStr="${2:0:4}-${2:4:2}-${2:6:2}"
DateMin=$(date -d $DateMinStr +%s)
DateMax=$(date -d $DateMaxStr +%s)

if [ $DateMin -ge $DateMax ]; then
    echo "ERROR: Minimum Date Cannot be above Maximum Date"
    exit
fi

DateCurrentStr=$DateMinStr
DateCurrent=$DateMin
while [ $DateCurrent -lt $DateMax ]
do
    TempMin=$(date -d $DateCurrentStr +%Y%m%d)
    TempMinStr=$(date -d $DateCurrentStr +%Y-%m-%d)
    TempMax=$(date -d "$TempMinStr + 1 month - 1 day" +%Y%m%d)
    TempMaxStr=$(date -d "$TempMinStr + 1 month - 1 day" +%Y-%m-%d)
    echo $TempMin $TempMax
    DateCurrent=$(date -d "$TempMaxStr + 1 day" +%s)
    DateCurrentStr=$(date -d "$TempMaxStr + 1 day" +%Y-%m-%d)
done

이 예제는 여러분이 원하는 것을 정확히 수행하지 않고 최적화와도 거리가 멀다고 생각하지만 이를 수행하는 방법에 대한 아이디어를 제공합니다.

답변2

유사한 도구날짜 도구일반적으로 이러한 종류의 작업에 더 적합하지만 간결성을 위해 여기에 있는 매개변수 세트와 다른 매개변수 세트를 사용하여 쉘 프리미티브를 사용하여 수행할 수 있습니다.

#!/bin/sh
# For a date range print start and end date per calendar month.

# Gregorian calendar: number of days in month.
# $1 year (>= 1600)
# $2 month (1..12), one leading '0' allowed for 1..9
# exitval: day count (28..31)
daysinmonth() {
    case "$2" in
    (2|02)
        test 0 -eq $(($1 % 4 || !($1 % 100) && $1 % 400))
        return $((28 + ($?<1))) ;;
    (4|6|9|11|04|06|09)
        return 30 ;;
    (*) return 31 ;;
    esac
}

# Print year,month,day in ISO 8601 basic format
isobasic() {
    printf '%d%02d%02d' "$1" "$2" "$3"
}

ylo="$1" mlo="${2#0}" dlo="${3#0}"      # range start
yhi="$4" mhi="${5#0}" dhi="${6#0}"      # range end
isohi=$(isobasic "${yhi}" "${mhi}" "${dhi}")
until 
    isolo=$(isobasic "${ylo}" "${mlo}" "${dlo}")
    test $(( isolo - isohi )) -gt 0
    # test "${isolo}" \> "${isohi}"     # dash, bash
do
    # compute end of month
    daysinmonth "${ylo}" "${mlo}"
    isoeom=$(isobasic "${ylo}" "${mlo}" "${?}")
    # print start, end date for partial/whole calendar month
    test "${isoeom%??}" != "${isohi%??}" || isoeom="${isohi}"
    printf '%s%s %s\n' "${PREFIX:-}" "${isolo}" "${isoeom}"
    # increase *lo values to first of next month
    dlo=1
    : $(( mlo = (mlo == 12 ? 1 : mlo + 1) ))
    : $(( ylo = (mlo > 1 ? ylo : ylo + 1) ))
done
# fail if start later than end date
return $(( isoeom == isohi ? 0 : 1 ))

주어진 명령

PREFIX='cmd ' ./script 2016 11 09 2020 3 5

출력은 다음과 같습니다

cmd 20161109 20161130
cmd 20161201 20161231
cmd 20170101 20170131
cmd 20170201 20170228
...
cmd 20191201 20191231
cmd 20200101 20200131
cmd 20200201 20200229
cmd 20200301 20200305

관련 정보