batch file과 shell script로 일수 계산 (그레고리안 달력) 스크립트를 만들어 봤습니다.

batch file과 shell script로 일수 계산 (그레고리안 달력) 스크립트를 만들어 봤습니다.
두대의 백업 서버를 번갈아 가면서 하루는 1번, 그 다음날은 2번 서버로 교대로 백업되고,
클라이언트 PC에서도 백업된 서버 중 지난날 백업된 서버로 자동 선택 접속되게 하려는데...
마땅히 아이디어가 떠오르지 않더군요.
날짜를 가지고 홀수 일자, 짝수 일자로 나눌 경우에 31일 다음 1일, 29일 다음 1일이 되어 버려 홀수 일자가 2번 반복되는 문제가 생기고, 요일 기준은 일주일이 7일이라 홀짝 번갈아 선택되게 하기 어렵고...
그래서 서기 1년 1월 1일 부터 현재일까지 총 일수를 계산해서 그 일수로 홀짝으로 번갈아 작동되게  하는 방법이 확실할 것 같아 윈도 PC에서 사용할 Batch file과 Linux 서버에서 작동 시킬 Shell script를 만들게 되었습니다.

<그레고리 력 - Gregorian Calendar 일자 계산 스크립트>

먼저 Batch file입니다.

File 명을 calc_days.bat라 정했습니다.

======== 피호출용 sub batch file : file 명 calc_days.bat
@echo off
rem 호출하는 배치 File에서 넘겨 주는 일자는 YYYY-MM-DD로 calc_days.bat %date%와 같이 호출
rem 넘겨 받은 파라미터 값에서 년/월/일 분리
for /f "tokens=1-3 delims=- " %%A in ('echo %1') do (set YYYY=%%A&SET MM=%%B&SET DD=%%C)
rem 년도
set year=%YYYY%
rem 월의 앞자리 0 제거 ( 1월~9월 까지 01~09의 값이 전달되어 숫자 앞에 0이 붙을 경우 8진수(?) 처리 )
rem 숫자 앞에 0이 붙어 있을 경우 잘못된 결과가 산출되어 숫자앞의 0을 제거함
for /f "tokens=* delims=0" %%a in ( 'echo %MM%') do set month=%%a
rem 일의 앞자리 0 제거 ( 월과 마찬가지로 1일~9일까지는 01~09로 전달되어 앞의 0 제거 )
for /f "tokens=* delims=0" %%a in ( 'echo %DD%') do set day=%%a

rem 이전 년도 계산 ( 이전 연도 연말 12월 31일까지 총일수 계산하기 위함 )
set /a ayear=( %year% - 1 )
rem 1년전 12월 31일까지의 총일수 ( 1년은 365일이나 윤년엔 2월이 28일이 아닌 29일이되어 366일이됨 )
rem 그레고리력에서 윤년은 4년 마다 한번씩인데, 100년 마다는 윤년처리하지 않고
rem 다시 400년 마다는 윤년 처리
set /a days = %ayear% * 365 + %ayear%/4 - %ayear%/100 + %ayear%/400

rem 이전달 계산 ( 지난해 연말까지 일수에 해당 년도의 이전달 말일까지 총일수를 더하기 위함 )
set /a amonth = %month% - 1
rem 1월 부터 해당 월의 이전 개월까지 월 마지막일을 더하여 총일수 더함
set i=1
rem batch file에서는 function, subroutine 등의 기능이 없어 goto 문을 사용하여 처리
rem 반복의 시작 시점 지정 
rem for문을 사용하여 반복처리하려 했으나 for문 처리 방식이
rem 일반 언어의 for문 처리 방식과 달라 큰 충격을 먹음
rem for문으로 처리하려다 잘안되어 많은 시간을 소비하다 뒤늦게 batch file for문의 작동방식을 깨닫고
rem for문 대신 goto 문으로 작성하게 됨
:loop
rem 1월 부터 이전 달까지 반복하면서 각 월의 일수를 더함 ( 월값 i가 이전달 보다 크면 반복 종료 )
if %i% gtr %amonth% goto :endloop
rem 월 별 마지막일 계산
rem 불행 중 다행 goto 문 대산 call 문이 있어 subroutine 비슷하게 처리할 수 있음
rem 점프할 label 명을 변수 조합으로 설정할 수 있어 불행 중 아주 큰 다행임
rem 배치 file에도 배열 기능이 있어 사용하려 했으나 잘 안되어 여러번 시도하다 포기했음
  call :mon%i%

rem 이전 년도 연말까지의 총일수에 계산일 이전 월의 말일까지 일수를 누적 더함
  set /a days+=%d%

  set /a i+=1
  goto :loop
rem 반복의 끝
:endloop

rem 마지막으로 해당월의 일자를 더해 총일수 얻음
set /a days+=%day%

rem 총일수를 종료 코드로 전달하고 스크립트 종료
exit /b %days%

rem 아래는 서버 루틴 개념으로 처리, 각 월별 마지막일 찾는 목적
rem 1월 마지막일
:mon1
  set d=31
rem goto :eof 명령을 사용하여 return과 같은 효과를 얻음
  goto :eof

rem 2월 마지막일
rem 평년일 경우 28일, 윤년일 경우 29일
rem 그레고리력 계산에 가장 중요한 부분인 2월 일수 계산
:mon2
rem 기본값으로 평년의 일수인 28일 지정
  set d=28
  set /a ck_leap=$year%/4
rem 4년의 배수에 해당할 경우라면
  if ck_leap == 0 (
     set /a ck_leap = %year% / 100
rem 100년의 배수에 해당할 경우라면
     if ck_leap neq 0 (
rem 400년의 배수에 해당할 경우라면
        set /a ck_leap = %year% / 400
        if ck_leap == 0 (
rem 4의 배수에 해당되고 100의 배수에 해당되지 않고 400의 배수에 해당된다면
rem 윤달이 되어 2월이 29일이 됨
           set d=29
        )
     )
  )
  goto :eof

rem 3월 마지막일
:mon3
  set d=31
  goto :eof


rem 4월 마지막일
:mon4
  set d=30
  goto :eof


rem 5월 마지막일
:mon5
  set d=31
  goto :eof

rem 6월 마지막일
:mon6
  set d=30
  goto :eof


rem 7월 마지막일
:mon7
  set d=31
  goto :eof


rem 8월 마지막일
:mon8
  set d=31
  goto :eof


rem 9월 마지막일
:mon9
  set d=30
  goto :eof

rem 10월 마지막일
:mon10
  set d=31
  goto :eof

rem 11월 마지막일
:mon11
  set d=30
  goto :eof

rem 12월 마지막일 - 불필요함(호출될일 없음)
rem 빼면 서운한 느낌이라 그냥 조금 할애해 줌
:mon12
  set d=31
  goto :eof
========================

위의 배치 file을 이용는 샘플 프로그램으로 요일을 계산하는 호출 프로그램을 만들어 보면...

===== 호출하는 batch file ( 요일 계산 sample )
@echo off
rem 화면에 명령이 표시되지 않게 하기 위해 echo off 사용
rem echo off 명령 조차 보이지 않게하기 위해 앞에 @ 붙임
rem 위에서 만든 batch file 호출시 call 명령을 사용해서 호출해야 위의 배치 file 수행 후
rem 호출한 batch file로 다시 돌아와서 다음 명령( 요일을 표시해주는 처리 )을 수행하게됨
rem 만약 call 없이 calc_days.bat %date% 명령을 사용한다면 calc_days.bat 수행후
rem 배치 file이 종료되어 버림
call calc_days.bat %date%

rem 앞에서 계산한 총일수를 7로 나눈 나머지로 요일 판별함
set /a week=%errorlevel% %% 7
rem echo %week%
if %week% equ 0 ( echo 일 )
if %week% equ 1 ( echo 월 )
if %week% equ 2 ( echo 화 )
if %week% equ 3 ( echo 수 )
if %week% equ 4 ( echo 목 )
if %week% equ 5 ( echo 금 )
if %week% equ 6 ( echo 토 )
===============
달력을 만들려면 월의 1일의 요일을 계산해서 1일 부터 쭉~ 뿌려 주면 되겠고...
두 일짜 사이의 일수를 계산한다면 두일자에 대해 각각 calc_days.bat를 호출하여 총일수를 구한후
두 결과 일수를 빼주면 되겠고요~


이제는  앞의 배치 파일과 유사하게 적용해서
linux bash shell script를 작성한 내용을 올리도록 하겠습니다.
기본 작동 내용을 배치 파일에서 설명을 해 두었기 때문에 여기서는 설명을 줄였습니다.
========== 피호출용 sub script file : file 명 calc_days.sh
#!/bin/bash
# 월별 마지막일(월의 일수)을 return하는 function
# batch file과 달리 shell script에는 function이 있어 function 기능을 사용함
# 여기서도 배열을 사용해 볼까 했으나 그냥 가능한 앞의 배치 파일과 유사하게 작성함
# shell script의 function도 C의 function과 유사하게 앞쪽에서 먼저 정의해두어야만 사용할 수 있음
# function이 호출하는 명령 뒤쪽에 있을 경우 function을 인식하지 못해 호출하지 못함
function mon_lday() {
#함수 호출 첫 번째 Parameter 값 처리 ( 월의 마지막일자을 찾기 원하는 월 전달 받음 )
 case  $1 in
#1월이면
   1)
     return 31
# rerun에 의해 function이 종료되기 때문에 break 명령은 불필요할 것이나 그냥 넣어둠
     break
     ;;
#2월이면
   2)
     d=28
#`expr $year / 4` 명령은 $(expr $year / 4)로 대체하여도 무방함
# `는 따옴표 '가 아닌 키보드 좌측 상단의 ESC 아래에 있는 "~" 키에 할당된 문자임
     ck_leap=`expr $year / 4`
     if [ $ck_leap = 0 ]; then
        ck_leap=`expr $year / 100`
        if [ $ck_leap != 0 ]; then
           ck_leap=`expr $year / 400`
           if [ ck_leap = 0 ]; then
              d=29
           fi
        fi
     fi
     return $d
     break
     ;;
#3월이면
   3)
     return 31
     break
     ;;
#4월이면
   4)
     return 30
     break
     ;;
#5월이면
   5)
     return 31
     break
     ;;
#6월이면
   6)
     return 30
     break
     ;;
#7월이면
   7)
     return 31
     break
     ;;
#8월이면
   8)
     return 31
     break
     ;;
#9월이면
   9)
     return 30
     break
     ;;
#10월이면
   10)
     return 31
     break
     ;;
#11월이면
   11)
     return 30
     break
     ;;
#12월이면-12월이 입력될 경우는 없어 12월은 불필요함
#여기서도 서운해할까 싶어 그냥 할애 해 줌 ^^;;
   12)
     return 31
     break
     ;;
 esac
}
#월별 마지막일 계산 Function 끝

#메인 스크립트 시작
#indate=$(date +%Y-%m-%d)
#전달된 Parameter 얻음
indate=$?
#indate="2018-01-09"

#전달된 파라미터에서 년도 찾음
year=$(echo $indate | cut -d "-" -f1)
#전달된 파라미터에서 월 찾음
month=$(echo $indate | cut -d "-" -f2)
#월의 숫자 앞부분에 있는 0 (zero) 제거
#shell script에서도 숫자 앞에 0이 있을 경우 숫자 계산을 제대로 하지 못함
month=$(echo $month | sed 's/^0*//')

#전달된 파라미터에서 일 찾음
day=$(echo $indate | cut -d "-" -f3)
#일의 숫자 앞부분에 있는 0 제거
day=$(echo $day | sed 's/^0*//')

#이전 년도 계산
# `expr $year - 1`은 $(expr $year -1 )로 대체 가능
ayear=`expr $year - 1`

#이전 년도 마지막일 ( 12월 31일 ) 까지 총 일수 계산
days=`expr $ayear \* 365 + $ayear / 4 - $ayear / 100 + $ayear / 400`

#이전 월 계산
amonth=`expr $month - 1`

# 이전 월까지 해당 년도의 총 일수 계산 ( 1월 부터 이전 월까지 반복 계산 )
# batch file과 달리 shell script에서는 for문 사용하여 반복 처리
# shell script에는 goto문이 없음
for (( i=1; i <= $amonth; ++i ))
do
# 각 월별 function mon_lday를 호출하여 해당 월의 총일수 계산
# 아래 명령은 다른 언어 함수 호출시 사용하는 mon_lday( $i )와 같은 역할
  mon_lday $i
# mon_lday() 함수의 결과 값을 앞에서 계산한 총일수에 누적
  days=`expr $days + $?`
done

# 이전 년도의 마지막일까지 일수 + 해당 년도의 이전 월 마지막일까지 일수 더한 값에 해당 월의 일 수 더함
days=`expr $days + $day`

#계산한 총일수를 exit code로 전달
#exit $days
# exit 명령으로 호출한 script로 값을 전달하려 하였으나 exit 종료 코드 값이 1byte로 제한된 것으로 보임
# 255 이상의 값을 처리하지 못해 여기서 echo 명령으로 결과를 출력하고
# 호출하는 script에서 $( ) 를 사용하여 받아가는 형식으로 처리하게 만듬
echo $days
==============================

Shell script도 batch file에서와 같이 호출하는 스크립트 샘플을 만들어 보았습니다.

=========== 호출하는 shell script ( 요일 계산 sambple )
#!/bin/bash
r=$(./calc_days.sh $(date +%Y-%m-%d))
#echo $r
week=$(expr $r % 7)
echo $week
if [ $week -eq 0 ]; then echo 일; fi
if [ $week -eq 1 ]; then echo 월; fi
if [ $week -eq 2 ]; then echo 화; fi
if [ $week -eq 3 ]; then echo 수; fi
if [ $week -eq 4 ]; then echo 목; fi
if [ $week -eq 5 ]; then echo 금; fi
if [ $week -eq 6 ]; then echo 토; fi
=================================

=========== 부분은 배치 file 및 스크립트의 시작부분을 표시한 것이며
배치 파일 및 스크립트 내용에 들어 가면 안됩니다. ^^

업무에 도움이 되시길 바랍니다~ ^^

3개의 댓글이 있습니다.

5년 이상 전

참고가 될만한 내용이 많네요. 공유 감사합니다.

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

5년 이상 전 | 유피드 | 010-4872-5959

좋은 자료 감사합니다.

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

5년 이상 전

감사합니다.

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입