シェルで2つの日付を比較する方法は?


88

シェルで2つの日付をどのように比較できますか?

そのままでは機能しませんが、これをどのように使用したいかの例を次に示します。

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

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

望ましい結果を得るにはどうすればよいですか?


何のためにループしたいですか?ループではなく条件付きですか?
マット

なぜファイルにタグを付けたのですか?それらの日付は実際にファイル時間ですか?
マナトワーク

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
誰かがこれを否定した理由はありません。それはかなり正確で、stringsい文字列の連結を処理しません。日付をUNIXタイムスタンプに変換し(秒単位)、UNIXタイムスタンプを比較します。
ニチョリ

このソリューションの主な利点は、加算または減算を簡単に行えることです。たとえば、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

ループ構造も欠落しています。より多くの日付を取得する予定はありますか?


これはdate、macOSに同梱されているものでは機能しません。
Flimm 16

date -d非標準であり、一般的なUNIXでは機能しません。BSDでは、カーネル値を設定しようとしますDST
18

32

標準の文字列比較を使用して、[年、月、日の形式の文字列の]時系列の順序を比較できます。

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

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

ありがたいことに、[YYYY-MM-DD形式を使用する文字列]がアルファベット順に*ソートされている場合、それらは時系列に*ソートされています。

(*-ソートまたは比較)

この場合、空想は必要ありません。わーい!


elseブランチはどこにありますか?
ウラジミールDespotovic

これは、シエラMacOSで私のために働いた唯一のソリューションです
ウラジミールDespotovic

これは私の解決策よりも簡単if [ $(sed -e s/-//g <<< $todate) -ge $(sed -e s/-//g <<< $cond) ];です。この日付形式は、標準の英数字の並べ替えを使用して並べ替えられるように設計されています-
ctrl-alt-delor

これは、ここで最も賢明な答えです
エドランドール

7

これはループ構造の問題ではなく、データ型の問題です。

これらの日付(todateおよびcond)は文字列であり、数値ではないため、の「-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ブランチはどこにありますか?
ウラジミールDespotovic

2
これが最も正しい答えのようです。非常に役立ちます!
Mojtaba Rezaeian

1

文字列をunix-timestamps(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 16

あなたの前に投稿された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 16

あなたが醸造を経てMacOSの上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")

これにより、1つのフォークのみを使用$todateして$cond、両方の変数とが再入力され、1つの日付を1行ずつ読み取るdate -f -ためにstdioが使用さます

最後に、ループを壊すことができます

((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
}

使い方の組み込みprintfウィッヒすることで、日付時刻をレンダリングすることができエポックからの秒見る(man bash;-)

このスクリプトは1つのフォークのみを使用します。

制限されたフォークと日付リーダー機能を備えた代替

これにより、専用のサブプロセス(1つのフォークのみ)が作成されます。

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.ハウリ

0

日付は整数ではなく文字列です。標準の算術演算子と比較することはできません。

セパレーターが保証されている場合、使用できる1つのアプローチ-

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.