날짜처리_04 윤년인지 확인하기

 
  • 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어

명령어: expr, test, ls
키워드: 윤년, 나머지
사용처: 2월 말에 작성된 로그 파일을 선택하고 싶을 때


실행예제

$ ./leapyear.sh
leap year: 2012
/var/log/myapp/access.log-20120229

스크립트

#!/bin/sh

# 네 자리 년도 얻기
year=$(date '+%Y')  #--------------------------------------- 1

logfile="/var/log/myapp/access.log-"

# 년도를 나눈 나머지 계산
mod1=$(expr $year % 4)  #----------------------------------- 2
mod2=$(expr $year % 100)  #--------------------------------- 2
mod3=$(expr $year % 400)  #--------------------------------- 2

# 윤년인지 판정
if [ $mod1 -eq 0 -a $mod2 -ne 0 -o $mod3 -eq 0 ]; then  #--- 3
    echo "leap year: $year"
    ls "${logfile}${year}0229"  #--------------------------- 4
else
    echo "not leap year:$year"
    ls "${logfile}${year}0228"  #--------------------------- 5
fi

   

해설

이 스크립트는 2월 말일 파일을 표시하기 위해 올해가 윤년인지 판별합니다. access.log-20120315 처럼 파일명에 년월일이 있는 로그 파일이 /var/log/myapp 디렉터리에 저장되어 있다고 가정합니다.

월말에 로그 파일을 참조해서 월별 처리를 하는 일이 많을 겁니다. 예제처럼 파일명에 날짜가 적힌 로그 파일을 다룰 때 2월 말일인지 판단하는 것은 프로그래머의 공통된 고민거리입니다.

우선 윤년인지 판정하는 조건을 확인해봅시다.

  1. 서력이 4로 나눠 떨어지면 윤년
  2. 단, 100으로 나눠 떨어지면 윤년이 아님
  3. 단, 400으로 나눠 떨어지면 윤년

특히 세 번째 조건은 알 알려지지 않아서 2000년에 많은 버그가 발생했던 원인이 되기도 했습니다.

우선 1에서 서력을 네 자리로 취득해서 셸 변수 year에 대입합니다. 2에서 년도를 나눈 나머지를 expr 명령어로 계산합니다. expr 명령어로 % 연산자를 써서 나머지를 계산하는데 조건 1, 2, 3의 각 나머지 값을 셸 변수 mod1, mod2, mod3에 대입합니다.

3에서는 앞에선 본 윤년 조건을 if문으로 판정합니다. 나눠 떨어진다라는 것은 나머지가 0이라는 뜻이므로 0과 변숫값을 비교합니다. 값이 같은지는 -eq 연산자로, 같지 않은지는 -ne 연산자로 판별합니다. -a는 AND, -o는 OR을 뜻합니다. 3조건식은 “mod1 값이 0이고 mod2 값은 0이 아니다” 또는 “mod3이 0”이라는 조건을 뜻합니다. test 명령어의 수치 판정 연산자는 다음 표를 참조하기 바랍니다.

  • test 명령어 비교 연산자

    표기 의미
    변수1 -eq 변수2 변수1과 변수2가 같으면 참
    변수1 -ne 변수2 변수1과 변수2가 다르면 참
    변수1 -lt 변수2 변수1이 변수2 미만이면 참
    변수1 -le 변수2 변수1이 변수2 이하이면 참
    변수1 -gt 변수2 변수1이 변수2 초과면 참
    변수1 -ge 변수2 변수1이 변수2 이상이면 참

lt는 “less than”, le는 “less than or equal to”, gt는 “greater than”, ge는 “greater than or equal to”입니다.

이렇게 하면 윤년 판정이 되므로 2월 말일 파일을 선택할 수 있습니다.
45에서 윤년일 때는 2월 29일 로그 파일을, 윤년이 아니면 2월 28일 로그 파일을 ls 명령어로 표시합니다.

   

주의사항

  • test 명령어의 -a(AND)와 -o(OR) 연산자 우선 순위는 다른 프로그래밍 언어와 마찬가지로 AND가 OR보다 우선입니다. 만약 OR 우선도를 AND보다 높이고 싶으면 괄호로 둘러쌉니다. 이때 셸이 괄호를 해석하지 않도록 \로 이스케이프해서 (… )처럼 사용합니다.
    # 앞에 있는 -a(AND)보다 -o(OR)가 우선 처리됨
    if [ $a -eq 0 -a \ ( $b -ne 0 -o $c -eq 0 \) ]; then
    

    “$a가 0이고, 4b가 0이 아니거나 $c가 0”일 때 참이 됩니다. test 명령어는 셸 내장과 외부 명령어 두 종류가 있는데 셸 스크립트는 셸 내장 test 명령어가 사용됩니다. 따라서 man sh로 셸 매뉴얼을 보면 test 명령어 설명이 나옵니다.