Bir kabuktaki iki tarih nasıl karşılaştırılır?


88

İki tarih bir kabuğun içinde nasıl karşılaştırılabilir?

İşte, nasıl çalıştığını göstermeme bir örnek:

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

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

İstenilen sonucu nasıl elde edebilirim?


Ne için döngü olmak istiyorsun? Döngü yerine şartlı mı demek istiyorsun?
Mat,

Neden dosyaları etiketlediniz ? Bu tarihler aslında dosya zamanları mı?
manatwork

Bu stackoverflow göz atın stackoverflow.com/questions/8116503/...
slm

Yanıtlar:


107

Doğru cevap hala eksik:

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

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

Bunun GNU tarihini gerektirdiğine dikkat edin. dateBSD'nin eşdeğer sözdizimi date(varsayılan olarak OSX'te bulunan gibi)date -j -f "%F" 2014-08-19 +"%s"


10
Neden birisinin bunu reddettiğini bilmiyoruz, bu oldukça doğru ve çirkin dizeleri birleştirmekle ilgilenmiyor. Tarihinizi unix zaman damgasına dönüştürün (saniye olarak), unix zaman damgalarını karşılaştırın.
Nicholi

Sanırım, bu çözümün temel avantajı, kolayca toplama veya çıkarma yapabilmenizdir. Örneğin, x saniye zaman aşımına sahip olmak istedim, ancak ilk fikrim ( timeout=$(`date +%Y%m%d%H%M%S` + 500)) kesinlikle yanlış.
iGEL

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

32

Karşılaştırma için tarih biçimini kaçırıyorsunuz:

#!/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

Siz de döngü yapıları eksik, daha fazla tarih almayı nasıl planlıyorsunuz?


Bu, dateMacOS ile birlikte gelenlerle çalışmaz .
Flimm

date -dstandart değildir ve tipik bir UNIX'te çalışmaz. BSD'de kenel değerini bile ayarlamaya çalışır DST.
schily

32

[Bir yıl, ay, gün biçimindeki dizelerin kronolojik sıralamasını] karşılaştırmak için standart bir dizi karşılaştırması kullanabilir.

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

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

Neyse ki, [YYYY-AA-GG formatını kullanan dizeler] alfabetik sıraya göre sıralanırsa *, kronolojik sıraya göre sıralanır *.

(* - sıralanmış veya karşılaştırılmış)

Bu durumda fantezi gerek yok. yuppi!


Burada başka şube nerede?
Vladimir Despotovic

Bu benim için Sierra MacOS'ta çalışan tek çözüm
Vladimir Despotovic

Bu benim çözümün daha basittir if [ $(sed -e s/-//g <<< $todate) -ge $(sed -e s/-//g <<< $cond) ];… Bu tarih formatı, standart alfa sayısal sıralama kullanılarak sıralanacak veya sayısal -olarak sıyrılacak ve sıralanacak şekilde tasarlanmıştır.
ctrl-alt-delor

Bu çok uzakta ve buradaki en akıllıca cevap
Ed Randall

7

Bu, döngüsel yapıların değil, veri türlerinin bir problemidir.

Bu tarihler ( todateve cond) sayı değil sayıdır, yani "-ge" operatörünü kullanamazsınız test. (Köşeli parantez notasının komuta eşdeğer olduğunu unutmayın test.)

Yapabileceğiniz şey, tarihleriniz için tamsayı olmaları için farklı bir gösterim kullanmak. Örneğin:

date +%Y%m%d

15 Temmuz 2013 için 20130715 gibi bir tamsayı üretecek. Tarihlerinizi "-ge" ve eşdeğer operatörlerle karşılaştırabilirsiniz.

Güncelleme: tarihleriniz belirtiliyorsa (örneğin, 2013-07-13 biçiminde bir dosyadan okuyorsanız), tr ile kolayca önişleme yapabilirsiniz.

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

6

Operatör, -geyalnızca tarihlerinizin bulunmadığı tam sayılarla çalışır.

Komut dosyanız bir bash veya ksh veya zsh komut dosyasıysa, <işleci kullanabilirsiniz . Bu operatör, POSIX standardının çok ötesine geçmeyen kısa çizgi veya diğer mermilerde mevcut değildir.

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

Herhangi bir kabukta, yalnızca tire işaretlerini kaldırarak tarih sırasına bakarken dizeleri sayılara dönüştürebilirsiniz.

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

Alternatif olarak, geleneksel olabilir ve expryardımcı programı kullanabilirsiniz .

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

Bir döngüdeki alt işlemlerin başlatılması yavaş olabileceğinden, dönüştürmeyi kabuk dizisi işlem yapılarını kullanarak yapmayı tercih edebilirsiniz.

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

Tabii ki, ilk önce tire işareti olmadan tarihleri ​​alabilirsiniz, bunları karşılaştırabilirsiniz -ge.


Bu bashtaki başka şube nerede?
Vladimir Despotovic

2
Bu en doğru cevap gibi görünüyor. Çok yararlı!
Mojtaba Rezaeian

1

Dizeleri unix-timestamps'a çeviririm (1.1.1970 0: 0: 0'dan beri saniye). Bunlar kolayca karşılaştırabilir

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

Bu dateMacOS ile birlikte gelenlerle çalışmıyor .
Flimm

Sizinkinden bir süre önce yayınlanan unix.stackexchange.com/a/170982/100397 ile aynı gözüküyor
roaima

1

Başlıklı makalede bu yöntem de var: BASH'de unix.com adresinden basit tarih ve zaman hesaplaması.

Bu fonksiyonlar bir betiğin !

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

kullanım

# 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"

Bu dateMacOS ile birlikte gelenlerle çalışmıyor .
Flimm

Eğer demlemek aracılığıyla MacOS üzerinde gdate yüklerseniz bu kolayca değiştirerek çalışmak için değiştirilebilir dateiçin gdate.
slm

1
Bu cevap davamda çok yardımcı oldu. Oranlamalı!
Mojtaba Rezaeian

1

özel cevap

Çatalları düşürmeyi sevdiğimden ve bir çok hile yapmaya izin verirse, amacım:

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

İyi şimdi:

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

Bu, her iki değişkenleri yeniden doldurur $todateve $condbir ouptut ile sadece bir çatal kullanarak, date -f -wich almak stdio hattı ile bir tarih okumak için.

Sonunda döngüsünü bozabilirsin

((todate>=cond))&&break

Veya bir işlev olarak :

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
}

Kullanılması 'nin yerleşik printfwich ile tarih saat render olabilir çağdan saniye (bkz man bash;-)

Bu komut dosyası yalnızca bir çatal kullanır.

Sınırlı çatal ve tarih okuyucu işleviyle alternatif

Bu özel bir alt işlem yaratacaktır (sadece bir çatal):

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

Giriş ve çıkış açık olduğundan, beş giriş silindi.

İşlev:

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

Not: Faydasız çatalları önlemek için todate=$(myDate 2013-07-18), değişken fonksiyonun kendisi tarafından ayarlanmalıdır. Ve serbest sözdizimine izin vermek için (tarih işaretine tırnaklı veya tırnaksız), değişken ismi son argüman .

Ardından tarih karşılaştırması:

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

yapabilir:

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

eğer bir döngünün dışındaysa.

Veya kabuk-bağlayıcı bash işlevini kullanın:

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

veya

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

( .shTamamen aynı değildir: kaynaklanmadıysa tam test senaryosu içerir)

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
}

Profil oluşturma bash , kullanmanın date -f -kaynak gereksinimini nasıl azaltabileceği konusunda iyi bir tanıtım içeriyor .
F. Hauri,

0

tarihler dizgedir, tamsayı değil; Onları standart aritmetik operatörleri ile karşılaştıramazsınız.

Ayırıcı olması garanti edilirse, kullanabileceğiniz bir yaklaşım -:

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

Bu kadar geriye çalışır bash 2.05b.0(1)-release.


0

Tarihleri ​​karşılaştırmak için mysql'nin yerleşik fonksiyonunu da kullanabilirsiniz. Sonucu 'günlerde' verir.

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

Sadece değerleri $DBUSERve $DBPASSWORDdeğişkenleri kendinize göre ekleyin .


0

FWIW: Mac'te, tarihe "2017-05-05" gibi bir tarihi beslemek şimdiki zamanı tarihe ekleyecek ve onu zamana dönüştürdüğünüz her seferde farklı bir değer elde edeceksiniz. sabit tarihler için daha tutarlı bir çağ süresi elde etmek için saat, dakika ve saniye için sahte değerler ekleyin:

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

Bu, macOS ile birlikte gönderilen tarihle sınırlı bir tuhaflık olabilir .... macOS 10.12.6'da test edilmiştir.


0

Echoing @Gilles answer ("operator -ge sadece tam sayılarla çalışır"), işte bir örnek.

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

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

datetimeepoch@ mike-q'nin cevabına göre https://stackoverflow.com/questions/10990949/convert-date-time-string-to-epoch-in-bash adresindeki tamsayı dönüşümleri :

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"büyük (son işaretinden büyük) "$old_date", ancak bu ifade yanlış olarak değerlendiriliyor False:

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

... açıkça gösterilmiştir, burada:

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

Tamsayılı karşılaştırmalar:

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

Bu karşılaştırmanın tirelenmiş bir tarih dizgisi ile iyi çalışmamasının nedeni, kabuğun baştaki 0 ​​olan bir sayının sekizlik olduğunu varsaymasıdır, bu durumda "07" ayı. Önerilen çeşitli çözümler olmuştur, ancak en hızlı ve en kolay tireleri çıkarmaktır. Bash, bu hızlı ve kolay kılan bir dize değiştirme özelliğine sahiptir, sonra karşılaştırma bir aritmetik ifade olarak yapılabilir:

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

if (( ${todate//-/} > ${cond//-/} ));
then
    echo larger
fi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.