쉘에서 두 날짜를 비교하는 방법은 무엇입니까?


88

쉘에서 두 날짜를 어떻게 비교할 수 있습니까?

다음은 그대로 사용하지 않지만 이것을 사용하고 싶은 방법의 예입니다.

todate=2013-07-18
cond=2013-07-15

if [ $todate -ge $cond ];
then
    break
fi                           

원하는 결과를 얻으려면 어떻게해야합니까?


무엇을 반복하고 싶습니까? 루프가 아닌 조건부를 의미합니까?
Mat

파일 을 태그 했습니까? 그 날짜는 실제로 파일 시간입니까?
manatwork

stackoverflow에서 이것을 확인하십시오. stackoverflow.com/questions/8116503/…
slm

답변:


107

정답은 여전히 ​​없습니다.

todate=$(date -d 2013-07-18 +%s)
cond=$(date -d 2014-08-19 +%s)

if [ $todate -ge $cond ];
then
    break
fi  

여기에는 GNU 날짜가 필요합니다. dateBSD에 대한 동등한 구문 date(기본적으로 OSX에 있음)은 다음과 같습니다.date -j -f "%F" 2014-08-19 +"%s"


10
왜 누군가가 이것을 다운 투표했는지 모르겠습니다. 정확하고 정확하지 않은 끈을 다루지 않습니다. 날짜를 유닉스 타임 스탬프 (초)로 변환하고 유닉스 타임 스탬프를 비교하십시오.
Nicholi

나는이 솔루션의 주요 장점은 더하기 또는 빼기를 쉽게 할 수 있다는 것입니다. 예를 들어 x 초의 시간 초과를 원했지만 첫 번째 아이디어 ( timeout=$(`date +%Y%m%d%H%M%S` + 500))가 분명히 잘못되었습니다.
iGEL

date -d 2013-07-18 +%s%N
typelogic

32

비교를위한 날짜 형식이 누락되었습니다.

#!/bin/bash

todate=$(date -d 2013-07-18 +"%Y%m%d")  # = 20130718
cond=$(date -d 2013-07-15 +"%Y%m%d")    # = 20130715

if [ $todate -ge $cond ]; #put the loop where you need it
then
 echo 'yes';
fi

루핑 구조가 누락되어 더 많은 날짜를 어떻게 계획하고 있습니까?


datemacOS와 함께 제공되는 제품 에서는 작동하지 않습니다 .
Flimm

date -d비표준이며 일반적인 UNIX에서는 작동하지 않습니다. BSD에서는 심지어 kenel 값을 설정하려고 시도합니다 DST.
schily

32

표준 문자열 비교를 사용하여 [연도, 월, 일 형식의 문자열 순서]를 비교할 수 있습니다.

date_a=2013-07-18
date_b=2013-07-15

if [[ "$date_a" > "$date_b" ]] ;
then
    echo "break"
fi

고맙게도 [YYYY-MM-DD 형식을 사용하는 문자열]이 알파벳 순서로 정렬 * 될 때도 시간순으로 정렬 *됩니다.

(*-정렬 또는 비교)

이 경우에는 멋진 것이 필요하지 않습니다. 예!


else 분기는 어디에 있습니까?
Vladimir Despotovic

이 시에라 맥 OS에 나를 위해 일한 유일한 솔루션입니다
블라디미르 Despotovic

이것은 내 솔루션이 더 간단합니다 if [ $(sed -e s/-//g <<< $todate) -ge $(sed -e s/-//g <<< $cond) ];...이 날짜 형식은 표준 알파벳 숫자 정렬을 사용하여 정렬하거나 숫자 -로 제거하고 정렬 하도록 설계되었습니다 .
ctrl-alt-delor

이것은 여기에 가장 현명한 답변입니다
Ed Randall

7

이는 루프 구조의 문제가 아니라 데이터 형식의 문제입니다.

해당 날짜 ( todatecond)는 숫자가 아닌 문자열이므로의 "-ge"연산자를 사용할 수 없습니다 test. (대괄호 표기법은 명령과 같습니다 test.)

할 수있는 일은 날짜에 다른 표기법을 사용하여 정수가되도록하는 것입니다. 예를 들면 다음과 같습니다.

date +%Y%m%d

2013 년 7 월 15 일에 대해 20130715와 같은 정수를 생성합니다. 그런 다음 날짜를 "-ge"및 동등한 연산자와 비교할 수 있습니다.

업데이트 : 날짜가 제공되면 (예 : 2013-07-13 형식의 파일에서 날짜를 읽는 경우) tr을 사용하여 쉽게 전처리 할 수 ​​있습니다.

$ echo "2013-07-15" | tr -d "-"
20130715

6

연산자 -ge는 날짜가 아닌 정수로만 작동합니다.

스크립트가 bash 또는 ksh 또는 zsh 스크립트 인 경우 <연산자를 대신 사용할 수 있습니다 . 이 연산자는 POSIX 표준을 크게 벗어나지 않는 대시 또는 다른 쉘에서는 사용할 수 없습니다.

if [[ $cond < $todate ]]; then break; fi

모든 셸에서 대시를 제거하여 날짜 순서를 준수하면서 문자열을 숫자로 변환 할 수 있습니다.

if [ "$(echo "$todate" | tr -d -)" -ge "$(echo "$cond" | tr -d -)" ]; then break; fi

또는 기존 방식으로 expr유틸리티를 사용할 수 있습니다 .

if expr "$todate" ">=" "$cond" > /dev/null; then break; fi

루프에서 하위 프로세스 호출이 느릴 수 있으므로 셸 문자열 처리 구문을 사용하여 변환을 수행하는 것이 좋습니다.

todate_num=${todate%%-*}${todate#*-}; todate_num=${todate_num%%-*}${todate_num#*-}
cond_num=${cond%%-*}${cond#*-}; cond_num=${cond_num%%-*}${cond_num#*-}
if [ "$todate_num" -ge "$cond_num" ]; then break; fi

물론 처음에 하이픈없이 날짜를 검색 할 수 있으면 날짜를와 비교할 수 있습니다 -ge.


이 bash에서 else 분기는 어디에 있습니까?
Vladimir Despotovic

2
이것이 가장 정답입니다. 매우 도움이되었습니다!
Mojtaba Rezaeian

1

문자열을 유닉스 타임 스탬프 (1.1.1970 0 : 0 : 0 이후의 초)로 변환합니다. 이들은 쉽게 비교할 수 있습니다

unix_todate=$(date -d "${todate}" "+%s")
unix_cond=$(date -d "${cond}" "+%s")
if [ ${unix_todate} -ge ${unix_cond} ]; then
   echo "over condition"
fi

datemacOS와 함께 제공되는 제품 에서는 작동하지 않습니다 .
Flimm

unix.stackexchange.com/a/170982/100397 과 같을 것 같습니다. 이전에 게시 된 시간
roaima

1

unix.com의 BASH 에서 간단한 날짜 및 시간 계산 이라는 제목의 기사 에서도이 방법이 있습니다.

이 함수는 해당 스레드스크립트에서 발췌 한 것입니다 !

date2stamp () {
    date --utc --date "$1" +%s
}

dateDiff (){
    case $1 in
        -s)   sec=1;      shift;;
        -m)   sec=60;     shift;;
        -h)   sec=3600;   shift;;
        -d)   sec=86400;  shift;;
        *)    sec=86400;;
    esac
    dte1=$(date2stamp $1)
    dte2=$(date2stamp $2)
    diffSec=$((dte2-dte1))
    if ((diffSec < 0)); then abs=-1; else abs=1; fi
    echo $((diffSec/sec*abs))
}

용법

# calculate the number of days between 2 dates
    # -s in sec. | -m in min. | -h in hours  | -d in days (default)
    dateDiff -s "2006-10-01" "2006-10-31"
    dateDiff -m "2006-10-01" "2006-10-31"
    dateDiff -h "2006-10-01" "2006-10-31"
    dateDiff -d "2006-10-01" "2006-10-31"
    dateDiff  "2006-10-01" "2006-10-31"

datemacOS와 함께 제공되는 제품 에서는 작동하지 않습니다 .
Flimm

당신이 양조를 통해 맥 OS에 gdate을 설치하는 경우이 쉽게 변경하여 작업을 수정할 수 있습니다 dategdate.
slm

1
이 답변은 제 사건에 매우 도움이되었습니다. 평가해야합니다!
Mojtaba Rezaeian

1

특정 답변

포크를 줄이고 가 많은 트릭을 허용하기 때문에 내 목적이 있습니다.

todate=2013-07-18
cond=2013-07-15

물론 지금:

{ read todate; read cond ;} < <(date -f - +%s <<<"$todate"$'\n'"$cond")

이 두 변수를 다시 채울 $todate$cond의 ouptut으로, 하나의 포크를 사용하여, date -f -느릅 나무 걸릴의 표준 입출력 라인으로 하나의 날짜를 읽는.

마지막으로 루프를 끊을 수 있습니다.

((todate>=cond))&&break

또는 기능으로 :

myfunc() {
    local todate cond
    { read todate
      read cond
    } < <(
      date -f - +%s <<<"$1"$'\n'"$2"
    )
    ((todate>=cond))&&return
    printf "%(%a %d %b %Y)T older than %(%a %d %b %Y)T...\n" $todate $cond
}

의 내장 printfwich를 사용하면 신기원에서 초 단위로 날짜 시간을 렌더링 할 수 있습니다 ( man bash;- 참조)

이 스크립트는 하나의 포크 만 사용합니다.

한정된 포크 및 날짜 판독기 기능을 가진 대안

전용 하위 프로세스가 생성됩니다 (단 하나의 포크).

mkfifo /tmp/fifo
exec 99> >(exec stdbuf -i 0 -o 0 date -f - +%s >/tmp/fifo 2>&1)
exec 98</tmp/fifo
rm /tmp/fifo

입력과 출력이 열려 있으면 fifo 항목이 삭제 될 수 있습니다.

함수:

myDate() {
    local var="${@:$#}"
    shift
    echo >&99 "${@:1:$#-1}"
    read -t .01 -u 98 $var
}

참고 : 와 같은 쓸모없는 포크를 방지하기 위해 todate=$(myDate 2013-07-18)변수는 함수 자체에 의해 설정됩니다. 그리고 무료 구문 (날짜 문자열에 따옴표 포함 또는 제외)을 허용하려면 변수 이름 이 마지막 인수 여야합니다 .

그런 다음 날짜 비교 :

myDate 2013-07-18        todate
myDate Mon Jul 15 2013   cond
(( todate >= cond )) && {
    printf "To: %(%c)T > Cond: %(%c)T\n" $todate $cond
    break
}

렌더링 할 수 있습니다 :

To: Thu Jul 18 00:00:00 2013 > Cond: Mon Jul 15 00:00:00 2013
bash: break: only meaningful in a `for', `while', or `until' loop

루프 외부인 경우.

또는 쉘 커넥터 bash 기능을 사용하십시오.

wget https://github.com/F-Hauri/Connector-bash/raw/master/shell_connector.bash

또는

wget https://f-hauri.ch/vrac/shell_connector.sh

(정확히 동일하지 않음 : .sh소싱되지 않은 경우 전체 테스트 스크립트를 포함)

source shell_connector.sh
newConnector /bin/date '-f - +%s' @0 0

myDate 2013-07-18        todate
myDate "Mon Jul 15 2013"   cond
(( todate >= cond )) && {
    printf "To: %(%c)T > Cond: %(%c)T\n" $todate $cond
    break
}

프로파일 링 bash 에는 date -f -리소스를 사용하여 리소스 요구 사항을 줄이는 방법에 대한 좋은 데모가 포함되어 있습니다 .
F. Hauri 2012

0

날짜는 정수가 아닌 문자열입니다. 표준 산술 연산자와 비교할 수 없습니다.

구분 기호가 보장되는 경우 사용할 수있는 한 가지 방법은 다음과 -같습니다.

IFS=-
read -ra todate <<<"$todate"
read -ra cond <<<"$cond"
for ((idx=0;idx<=numfields;idx++)); do
  (( todate[idx] > cond[idx] )) && break
done
unset IFS

이것은까지 거슬러 올라갑니다 bash 2.05b.0(1)-release.


0

mysql의 내장 함수를 사용하여 날짜를 비교할 수도 있습니다. 결과는 '일수'로 나타납니다.

from_date="2015-01-02"
date_today="2015-03-10"
diff=$(mysql -u${DBUSER} -p${DBPASSWORD} -N -e"SELECT DATEDIFF('${date_today}','${from_date}');")

귀하 의 값 $DBUSER$DBPASSWORD변수를 입력하십시오.


0

FWIW : Mac에서는 날짜에 "2017-05-05"와 같은 날짜를 입력하면 현재 시간이 날짜에 추가되고 시간을 에포크 시간으로 변환 할 때마다 다른 값을 얻게됩니다. 고정 날짜에 대해 더 일관된 에포크 시간을 얻으려면 시간, 분 및 초에 대한 더미 값을 포함하십시오.

date -j -f "%Y-%m-%d %H:%M:%S" "2017-05-05 00:00:00" +"%s"

macOS 10.12.6에서 테스트 한 macOS와 함께 제공되는 날짜 버전으로 제한 될 수 있습니다.


0

@Gilles 응답 에코 ( "연산자 -ge는 정수로만 작동")의 예는 다음과 같습니다.

curr_date=$(date +'%Y-%m-%d')
echo "$curr_date"
2019-06-20

old_date="2019-06-19"
echo "$old_date"
2019-06-19

datetimehttps://stackoverflow.com/questions/10990949/convert-date-time-string-to-epoch-in-bashepoch 의 @ mike-q 답변에 따라 정수로 변환 :

curr_date_int=$(date -d "${curr_date}" +"%s")
echo "$curr_date_int"
1561014000

old_date_int=$(date -d "${old_date}" +"%s")
echo "$old_date_int"
1560927600

"$curr_date"보다 큼 (보다 최신) "$old_date"이지만이 표현식은 False다음 과 같이 잘못 평가됩니다 .

if [[ "$curr_date" -ge "$old_date" ]]; then echo 'foo'; fi

... 여기에 명시 적으로 표시됩니다.

if [[ "$curr_date" -ge "$old_date" ]]; then echo 'foo'; else echo 'bar'; fi
bar

정수 비교 :

if [[ "$curr_date_int" -ge "$old_date_int" ]]; then echo 'foo'; fi
foo

if [[ "$curr_date_int" -gt "$old_date_int" ]]; then echo 'foo'; fi
foo

if [[ "$curr_date_int" -lt "$old_date_int" ]]; then echo 'foo'; fi

if [[ "$curr_date_int" -lt "$old_date_int" ]]; then echo 'foo'; else echo 'bar'; fi
bar

0

이 비교가 하이픈이있는 날짜 문자열에서 제대로 작동하지 않는 이유는 쉘에서 앞에 0이있는 숫자는 8 진수 (이 경우 "07") 인 것으로 가정하기 때문입니다. 제안 된 다양한 솔루션이 있지만 가장 빠르고 쉬운 방법은 하이픈을 제거하는 것입니다. Bash에는 문자열 대체 기능이있어이를 쉽고 빠르게 수행 할 수 있으며 산술 표현식으로 비교를 수행 할 수 있습니다.

todate=2013-07-18
cond=2013-07-15

if (( ${todate//-/} > ${cond//-/} ));
then
    echo larger
fi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.