शेल में दो तिथियों की तुलना कैसे करें?


88

एक शेल में दो तिथियों की तुलना कैसे की जा सकती है?

यहाँ एक उदाहरण दिया गया है कि मैं इसका उपयोग कैसे करना चाहूँगा, हालाँकि यह काम नहीं करता है:

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

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

मैं वांछित परिणाम कैसे प्राप्त कर सकता हूं?


क्या आप के लिए पाश करना चाहते हैं? क्या आपके पास लूप के बजाय सशर्त का मतलब है?
Mat

आपने फ़ाइलों को क्यों टैग किया ? क्या वे तिथियां वास्तव में फ़ाइल समय हैं?
मैनटवर्क

: इस 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  

ध्यान दें कि इसके लिए जीएनयू तिथि की आवश्यकता है। dateBSD के लिए बराबर सिंटैक्स date(जैसे डिफ़ॉल्ट रूप से OSX में पाया जाता है) हैdate -j -f "%F" 2014-08-19 +"%s"


10
डोनो ने किसी को यह अस्वीकार क्यों किया, यह बहुत सटीक है और बदसूरत तारों को समाप्‍त करने से नहीं निपटता है। अपनी तिथि को यूनिक्स टाइमस्टैम्प में परिवर्तित करें (सेकंड में), यूनिक्स टाइमस्टैम्प की तुलना करें।
निकोली

मुझे लगता है, मुझे इस समाधान का मुख्य लाभ यह है कि आप आसानी से जोड़ या घटाव कर सकते हैं। उदाहरण के लिए, मैं x सेकंड का टाइमआउट करना चाहता था, लेकिन मेरा पहला विचार ( timeout=$(`date +%Y%m%d%H%M%S` + 500)) स्पष्ट रूप से गलत है।
आईजीएल

आप nanoseconds परिशुद्धता के साथ शामिल कर सकते हैं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 के साथ शिप के साथ काम नहीं करता है ।
फ्लिम्स

date -dगैर मानक है और एक विशिष्ट यूनिक्स पर काम नहीं करता है। बीएसडी पर यह केनेल मूल्य निर्धारित करने का भी प्रयास करता है DST
श्लोक

32

एक वर्ष, महीने, दिन प्रारूप में तार के कालानुक्रमिक क्रम की तुलना करने के लिए एक मानक स्ट्रिंग तुलना का उपयोग कर सकते हैं।

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

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

शुक्र है, जब [तार जो YYYY-MM-DD प्रारूप का उपयोग करते हैं] को * वर्णानुक्रम में क्रमबद्ध किया जाता है, वे भी कालानुक्रमिक क्रम में * क्रमबद्ध किए जाते हैं।

(* - क्रमबद्ध या तुलना)

इस मामले में फैंसी कुछ भी नहीं चाहिए। वाह!


यहाँ दूसरी शाखा कहाँ है?
व्लादिमीर डेस्पोटोविच

यह एकमात्र समाधान है जिसने मेरे लिए सिएरा मैकओएस
व्लादिमीर डेस्पोटोविक

यह सरल है कि मेरा समाधान 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

15 जुलाई, 2013 के लिए 20130715 जैसे पूर्णांक का उत्पादन करेगा। फिर आप अपनी तारीखों की तुलना "-ge" और समकक्ष ऑपरेटरों के साथ कर सकते हैं।

अद्यतन: यदि आपकी तारीखें दी गई हैं (जैसे आप 2013-07-13 प्रारूप में उन्हें एक फ़ाइल से पढ़ रहे हैं) तो आप उन्हें आसानी से tr के साथ प्रीप्रोसेस कर सकते हैं।

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

6

ऑपरेटर -geकेवल पूर्णांकों के साथ काम करता है, जो आपकी तारीखें नहीं हैं।

यदि आपकी स्क्रिप्ट एक bash या ksh या zsh स्क्रिप्ट है, तो आप <इसके बजाय ऑपरेटर का उपयोग कर सकते हैं । यह ऑपरेटर डैश या अन्य शेल में उपलब्ध नहीं है जो पोसिक्स मानक से बहुत आगे नहीं जाता है।

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


इस बैश में दूसरी शाखा कहाँ है?
व्लादिमीर डेस्पोटोविच

2
यह सबसे सही उत्तर लगता है। बहुत उपयोगी!
मोजतबा रेज़ियन

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 के साथ उस जहाज के साथ काम नहीं करता है ।
फ्लिम्स

ऐसा लगता है जैसे unix.stackexchange.com/a/170982/100397 जो आपके सामने कुछ समय पहले पोस्ट किया गया था
roaima

1

लेख से यह विधि भी है: Bix में सरल तिथि और समय की समाप्ति unix.com से।

ये कार्य उस थ्रेड में एक स्क्रिप्ट से एक अंश हैं !

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 के साथ उस जहाज के साथ काम नहीं करता है ।
फ्लिम्स

यदि आप काढ़ा के माध्यम से MacOS पर gdate स्थापित करते हैं तो इसे आसानी से बदलकर काम करने के लिए संशोधित किया जा सकता dateहै gdate
स्लम

1
यह जवाब मेरे मामले पर बहुत मददगार था। यह दर करना चाहिए!
मोजतबा रेज़ियन

1

विशिष्ट जवाब

के रूप में मैं कांटे कम करने और पसंद करते हैं, बहुत सारे ट्रिक की अनुमति देते हैं, मेरा उद्देश्य है:

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

तो अब:

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

यह दोनों चर को फिर से पॉप्युलेट करेगा $todateऔर $condकेवल एक कांटे का उपयोग करके, 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

यह लिपि केवल एक कांटे का उपयोग करती है।

सीमित कांटे और तिथि पाठक समारोह के साथ वैकल्पिक

यह एक समर्पित उपप्रकार (केवल एक कांटा) बनाएगा:

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

इनपुट और आउटपुट खुले होने के कारण, पेंडो प्रविष्टि को हटाया जा सकता है।

कार्यक्रम:

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

अगर एक लूप के बाहर।

या शेल-कनेक्टर बैश फ़ंक्शन का उपयोग करें:

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
}

प्रोफाइलिंग बैश में एक अच्छा प्रदर्शन होता है कि कैसे उपयोग date -f -करने से रीसस की आवश्यकता कम हो सकती है।
एफ। हौरी

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

एफडब्ल्यूआईडब्ल्यू: मैक पर, यह "2017-05-05" की तरह ही एक तारीख को खिलाता है, तारीख में वर्तमान समय को तारीख में जोड़ देगा और आपको हर बार अलग-अलग मूल्य मिलेगा जो आप इसे युग में परिवर्तित करते हैं। निश्चित तिथियों के लिए अधिक सुसंगत युग प्राप्त करने के लिए, घंटे, मिनट और सेकंड के लिए डमी मान शामिल करें:

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

यह दिनांक के संस्करण तक सीमित एक विषमता हो सकती है जिसे macOS के साथ भेज दिया गया है .... macOS 10.12.6 पर परीक्षण किया गया।


0

गूंज @Gilles उत्तर ("ऑपरेटर केवल पूर्णांक के साथ काम करता है"), यहां एक उदाहरण है।

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

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

datetimeपूर्णांक रूपांतरण epoch, प्रति @ mike-q का उत्तर https://stackoverflow.com/questions/10990949/convert-date-time-string-to-epoch-in-bash पर :

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 के साथ एक संख्या है एक अष्टक है, इस मामले में महीने "07"। वहाँ विभिन्न समाधान प्रस्तावित किए गए हैं लेकिन सबसे तेज़ और आसान हाइफ़न को बाहर निकालना है। बैश में एक स्ट्रिंग प्रतिस्थापन सुविधा है जो इसे त्वरित और आसान बनाती है फिर तुलना एक अंकगणितीय अभिव्यक्ति के रूप में की जा सकती है:

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

if (( ${todate//-/} > ${cond//-/} ));
then
    echo larger
fi
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.