कैसे बैश का उपयोग कर तारीखों के माध्यम से पाश करने के लिए?


85

मेरे पास ऐसी बैश स्क्रिप्ट है:

array=( '2015-01-01', '2015-01-02' )

for i in "${array[@]}"
do
    python /home/user/executeJobs.py {i} &> /home/user/${i}.log
done

अब मैं तारीखों की एक सीमा के माध्यम से लूप करना चाहता हूं, जैसे 2015-01-01 से 2015-01-31 तक।

बाश में कैसे प्राप्त करें?

अपडेट :

नीस-टू-है: पिछले रन के पूरा होने से पहले कोई काम शुरू नहीं किया जाना चाहिए। इस स्थिति में, जब executeJobs.py पूरा हो जाता है तो bash शीघ्र $वापस आ जाएगा।

उदाहरण के लिए मैं wait%1अपने पाश में शामिल कर सकता हूं ?


क्या आप जीएनयू की तारीख वाले प्लेटफॉर्म पर हैं?
चार्ल्स डफी

1
इस लिंक की जाँच करें: glatter-gotz.com/blog/2011/02/19/…
qqibrow

1
BTW, चूंकि आपके पास पायथन इंटरप्रेटर काम है, यह datetimeपायथन मॉड्यूल का उपयोग करके विश्वसनीय और पोर्टेबल तरीके से करना बहुत आसान होगा ।
चार्ल्स डफी

3
2015-01-01 तक 2015-01-31 तक एक महीने से अधिक समय में तिथियां नहीं होती हैं, इसलिए यह एक बहुत ही सरल मामला है।
विंटरम्यूट

2
... इसलिए, यदि आप वास्तव में (जब आप नहीं करते हैं, तो समवर्ती प्रक्रियाओं के कारण होने वाली बग) की आवश्यकता को देख रहे हैं wait, तो आपके पास कुछ और अधिक दिलचस्प / अधिक जटिल चल रहा है, जिसे और अधिक जटिल समाधान की आवश्यकता है ( जैसे कि उपप्रकार को एक ताला लगाने की विरासत के लिए पूछना), जो पर्याप्त जटिलता है और अंकगणित की तारीख तक पर्याप्त रूप से असंबंधित है कि यह एक अलग प्रश्न होना चाहिए।
चार्ल्स डफी

जवाबों:


197

GNU दिनांक का उपयोग करना:

d=2015-01-01
while [ "$d" != 2015-02-20 ]; do 
  echo $d
  d=$(date -I -d "$d + 1 day")
done

ध्यान दें कि क्योंकि यह स्ट्रिंग तुलना का उपयोग करता है, इसके लिए किनारे की तारीखों के पूर्ण आईएसओ 8601 संकेतन की आवश्यकता होती है (अग्रणी शून्य को दूर न करें)। वैध इनपुट डेटा की जांच करने और इसे वैध रूप में ले जाने के लिए यदि संभव हो तो, आप इसका उपयोग कर सकते हैं date:

# slightly malformed input data
input_start=2015-1-1
input_end=2015-2-23

# After this, startdate and enddate will be valid ISO 8601 dates,
# or the script will have aborted when it encountered unparseable data
# such as input_end=abcd
startdate=$(date -I -d "$input_start") || exit -1
enddate=$(date -I -d "$input_end")     || exit -1

d="$startdate"
while [ "$d" != "$enddate" ]; do 
  echo $d
  d=$(date -I -d "$d + 1 day")
done

एक अंतिम अलावा : कि जांच करने के लिए $startdateइससे पहले कि है $enddate, अगर आप केवल वर्ष 1000 और 9999 के लिए, आप बस इस तरह स्ट्रिंग तुलना उपयोग कर सकते हैं के बीच तारीखों की उम्मीद:

while [[ "$d" < "$enddate" ]]; do

10000 से परे बहुत सुरक्षित पक्ष पर होने के लिए, जब लेक्सिकोग्राफिक तुलना टूट जाती है, उपयोग करें

while [ "$(date -d "$d" +%Y%m%d)" -lt "$(date -d "$enddate" +%Y%m%d)" ]; do

अभिव्यक्ति एक संख्यात्मक रूप में $(date -d "$d" +%Y%m%d)परिवर्तित $dहोती है, अर्थात, 2015-02-23बन जाती है 20150223, और विचार यह है कि इस रूप में तिथियों की तुलना संख्यात्मक रूप से की जा सकती है।


1
जरूर, क्यों नहीं। यह सिर्फ एक शेल लूप है, कि यह तारीखों का उपयोग करता है क्योंकि इट्रेटर नहीं बदलता है कि आप इसके अंदर क्या कर सकते हैं।
विंटरम्यूट

1
@SirBenBenji, ... ने कहा, %1एक नौकरी नियंत्रण निर्माण है, और नौकरी पर नियंत्रण को गैर-सक्रिय स्क्रिप्ट में बंद कर दिया जाता है जब तक कि आप स्पष्ट रूप से इसे स्वयं चालू नहीं करते हैं। स्क्रिप्ट के अंदर अलग-अलग उपप्रकारों को संदर्भित करने का उचित तरीका पीआईडी ​​द्वारा है, और फिर भी, प्रक्रियाओं को पूरा करने के लिए इंतजार करना स्वचालित है जब तक कि वे आपके कोड (जैसा कि &) के साथ स्पष्ट रूप से पृष्ठभूमि में नहीं होते हैं , या वे स्वयं-अलग (किस मामले में) waitयह भी काम नहीं करेगा, और शेल को दी गई पीआईडी ​​को डबल-फोर्क प्रक्रिया द्वारा स्व-पृष्ठभूमि के लिए अमान्य किया जाएगा)।
चार्ल्स डफी

1
करीब से देखने के बाद, ऐसा प्रतीत होता है कि लीप सेकंड को UNIX टाइमस्टैम्प से बाहर रखा गया है, ताकि कुछ टाइमस्टैम्प दो सेकंड के स्थान को संदर्भित करें। यह स्पष्ट रूप से उप-सेकंड रेंज में लागू करने के लिए apparentlygettimeofday` को बहुत दिलचस्प बनाता है, और मुझे लगता है कि हमें खुद को भाग्यशाली मानना ​​चाहिए कि लीप सेकंड को एक वर्ष से कभी नहीं हटाया गया है । इसका मतलब है कि मुझे खुद को ठीक करना है: एक यूनिक्स टाइमस्टैम्प में 86400 सेकंड जोड़ना यकीनन हमेशा एक दिन को जोड़ने के समान है, क्योंकि 2016-12-31 टी 23: 59: 60 विशेष रूप से संदर्भित करने का कोई तरीका नहीं है। टीआईएल।
विंटरम्यूट

2
अपना पहला कोड (sh test.sh) चलाने से मुझे एक त्रुटि मिलती है: दिनांक: अवैध विकल्प - I उपयोग: दिनांक [-jnRu] [-d dst] [-r सेकंड] [-t west] [-v [+ | - "वल [ymwdHMS]] ... [-f fmt date | [[[mm] dd] HH] MM [[cc] yy] [। ss]] [+ प्रारूप]
dorien

2
MacOS के लिए यह काम नहीं करेगा, पहले gnu date apple.stackexchange.com/questions/231224/…
Jaime Agudo

22

ब्रेस विस्तार :

for i in 2015-01-{01..31} …

अधिक:

for i in 2015-02-{01..28} 2015-{04,06,09,11}-{01..30} 2015-{01,03,05,07,08,10,12}-{01..31} …

प्रमाण:

$ echo 2015-02-{01..28} 2015-{04,06,09,11}-{01..30} 2015-{01,03,05,07,08,10,12}-{01..31} | wc -w
 365

कॉम्पैक्ट / नेस्ट:

$ echo 2015-{02-{01..28},{04,06,09,11}-{01..30},{01,03,05,07,08,10,12}-{01..31}} | wc -w
 365

आदेश दिया, अगर यह मायने रखता है:

$ x=( $(printf '%s\n' 2015-{02-{01..28},{04,06,09,11}-{01..30},{01,03,05,07,08,10,12}-{01..31}} | sort) )
$ echo "${#x[@]}"
365

चूंकि यह अनियंत्रित है, आप बस लीप वर्ष पर सौदा कर सकते हैं:

$ echo {2015..2030}-{02-{01..28},{04,06,09,11}-{01..30},{01,03,05,07,08,10,12}-{01..31}} {2016..2028..4}-02-29 | wc -w
5844

3
लीप वर्ष के बारे में क्या?
विंटरम्यूट

मैं तो उपयोग कर सकते हैं python /home/user/executeJobs.py 2015-01-{01..31} &> /home/user/2015-01-{01..31}.log ?
स्टीव के

@SirBenBenji जो निर्भर करता है executeJobs.py
कोजिरो

मुझे कहना होगा कि निष्पादित करेंजॉब्स को तिथि पैरामीटर की आवश्यकता है और मुझे पूरा करने के लिए प्रत्येक रन पर प्रतीक्षा करने की आवश्यकता है। यह एक बिग डेटा काम है और इससे पहले कि प्रत्येक रन पूरा होने से पहले शुरू नहीं किया जाना चाहिए। मुझे इसके बारे में पहले सोचना चाहिए था, इसके बारे में भूलने के लिए खेद है।
स्टीव के

10
start='2019-01-01'
end='2019-02-01'

start=$(date -d $start +%Y%m%d)
end=$(date -d $end +%Y%m%d)

while [[ $start -le $end ]]
do
        echo $start
        start=$(date -d"$start + 1 day" +"%Y%m%d")

done

3

मेरे पास एक ही मुद्दा था और मैंने उपरोक्त उत्तर में से कुछ की कोशिश की, शायद वे ठीक हैं, लेकिन उन उत्तरों में से कोई भी तय नहीं है कि मैं क्या करने की कोशिश कर रहा था, मैकओएस का उपयोग करके।

मैं अतीत में तारीखों पर पुनरावृति करने की कोशिश कर रहा था, और निम्नलिखित वह है जो मेरे लिए काम कर रहा है:

#!/bin/bash

# Get the machine date
newDate=$(date '+%m-%d-%y')

# Set a counter variable
counter=1 

# Increase the counter to get back in time
while [ "$newDate" != 06-01-18 ]; do
  echo $newDate
  newDate=$(date -v -${counter}d '+%m-%d-%y')
  counter=$((counter + 1))
done

आशा करता हूँ की ये काम करेगा।


मैं एक चर नाम का उपयोग नहीं करने की सलाह देता हूं जो बहुत शक्तिशाली बिट-फॉर-बिट कॉपी कमांड के साथ मेल खाता है, लेकिन यह सिर्फ मेरे लिए है।
मेरिलफ्रेज

मैकओएस के gdateबजाय सरल तरीका उपयोग करना है date
नॉर्थट्री

2

यदि कोई नीचे दी गई किसी भी सीमा तक इनपुट तिथि से लूप करना चाहता है, तो वह आउटपुट को yyyMMM के प्रारूप में प्रिंट करेगा ...

#!/bin/bash
in=2018-01-15
while [ "$in" != 2018-01-25 ]; do
  in=$(date -I -d "$in + 1 day")
  x=$(date -d "$in" +%Y%m%d)
  echo $x
done

2

मुझे AIX, BSDs, Linux, OS X और Solaris पर तारीखों के माध्यम से लूप करने की आवश्यकता थी। dateआदेश उपयोग करने के लिए कम से कम पोर्टेबल और सबसे दुखी आदेशों प्लेटफार्मों पर मैं सामना करना पड़ा से एक है। मुझे एक my_dateकमांड लिखना आसान लगा, जो हर जगह काम करती हो।

नीचे सी कार्यक्रम एक प्रारंभिक तिथि लेता है, और इसमें से दिनों को जोड़ता या घटाता है। यदि कोई दिनांक नहीं दी जाती है, तो यह वर्तमान तिथि से दिनों को जोड़ता या घटाता है।

my_dateआदेश आप निम्नलिखित हर जगह प्रदर्शन करने के लिए अनुमति देता है:

start="2015-01-01"
stop="2015-01-31"

echo "Iterating dates from ${start} to ${stop}."

while [[ "${start}" != "${stop}" ]]
do
    python /home/user/executeJobs.py {i} &> "/home/user/${start}.log"
    start=$(my_date -s "${start}" -n +1)
done

और सी कोड:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>

int show_help();

int main(int argc, char* argv[])
{
    int eol = 0, help = 0, n_days = 0;
    int ret = EXIT_FAILURE;

    time_t startDate = time(NULL);
    const time_t ONE_DAY = 24 * 60 * 60;

    for (int i=0; i<argc; i++)
    {
        if (strcmp(argv[i], "-l") == 0)
        {
            eol = 1;
        }
        else if (strcmp(argv[i], "-n") == 0)
        {
            if (++i == argc)
            {
                show_help();
                ret = EXIT_FAILURE;
                goto finish;
            }

            n_days = strtoll(argv[i], NULL, 0);
        }
        else if (strcmp(argv[i], "-s") == 0)
        {
            if (++i == argc)
            {
                show_help();
                ret = EXIT_FAILURE;
                goto finish;
            }

            struct tm dateTime;
            memset (&dateTime, 0x00, sizeof(dateTime));

            const char* start = argv[i];
            const char* end = strptime (start, "%Y-%m-%d", &dateTime);

            /* Ensure all characters are consumed */
            if (end - start != 10)
            {
                show_help();
                ret = EXIT_FAILURE;
                goto finish;
            }

            startDate = mktime (&dateTime);
        }
    }

    if (help == 1)
    {
        show_help();
        ret = EXIT_SUCCESS;
        goto finish;
    }

    char buff[32];
    const time_t next = startDate + ONE_DAY * n_days;
    strftime(buff, sizeof(buff), "%Y-%m-%d", localtime(&next));

    /* Paydirt */
    if (eol)
        fprintf(stdout, "%s\n", buff);
    else
        fprintf(stdout, "%s", buff);

    ret = EXIT_SUCCESS;

finish:

    return ret;
}

int show_help()
{
    fprintf(stderr, "Usage:\n");
    fprintf(stderr, "  my_date [-s date] [-n [+|-]days] [-l]\n");
    fprintf(stderr, "    -s date: optional, starting date in YYYY-MM-DD format\n");
    fprintf(stderr, "    -n days: optional, number of days to add or subtract\n");
    fprintf(stderr, "    -l: optional, add new-line to output\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "  If no options are supplied, then today is printed.\n");
    fprintf(stderr, "\n");
    return 0;
}

2

बैश को लीवरेजिंग पाइप (|) द्वारा लिखा गया है। यह स्मृति कुशल और समवर्ती (तेज) प्रसंस्करण में परिणाम होना चाहिए। मैं निम्नलिखित लिखूंगा:

seq 0 100 | xargs printf "20 Aug 2020 - %sdays\n" \
  | xargs -d '\n' -l date -d

निम्नलिखित इसकी तिथि को 20 aug 2020प्रिंट करेगा और इसके 100 दिन पहले की तारीखों को प्रिंट करेगा।

इस ऑनलाइनर को एक उपयोगिता में बनाया जा सकता है।

#!/usr/bin/env bash

# date-range template <template>

template="${1:--%sdays}"

export LANG;

xargs printf "$template\n" | xargs -d '\n' -l date -d

डिफ़ॉल्ट रूप से हम एक बार में पिछले 1 दिन में पुनरावृति चुन लेते हैं।

$ seq 10 | date-range
Mon Mar  2 17:42:43 CET 2020
Sun Mar  1 17:42:43 CET 2020
Sat Feb 29 17:42:43 CET 2020
Fri Feb 28 17:42:43 CET 2020
Thu Feb 27 17:42:43 CET 2020
Wed Feb 26 17:42:43 CET 2020
Tue Feb 25 17:42:43 CET 2020
Mon Feb 24 17:42:43 CET 2020
Sun Feb 23 17:42:43 CET 2020
Sat Feb 22 17:42:43 CET 2020

मान लीजिए कि हम एक निश्चित तारीख तक तारीखें उत्पन्न करना चाहते हैं। हम अभी तक नहीं जानते हैं कि हमें वहाँ कितने पुनरावृत्तियों की आवश्यकता है। मान लें कि टॉम का जन्म 1 जनवरी 2001 को हुआ था। हम एक निश्चित तिथि तक प्रत्येक तिथि को उत्पन्न करना चाहते हैं। हम इसे sed का उपयोग करके प्राप्त कर सकते हैं।

seq 0 $((2**63-1)) | date-range | sed '/.. Jan 2001 /q'

$((2**63-1))चाल एक बड़ा पूर्णांक बनाने के लिए प्रयोग किया जाता है।

एक बार सेड निकल जाने के बाद यह डेट-रेंज यूटिलिटी से भी बाहर निकल जाएगा।

एक 3 महीने के अंतराल का उपयोग करके भी पुनरावृति कर सकता है:

$ seq 0 3 12 | date-range '+%smonths'
Tue Mar  3 18:17:17 CET 2020
Wed Jun  3 19:17:17 CEST 2020
Thu Sep  3 19:17:17 CEST 2020
Thu Dec  3 18:17:17 CET 2020
Wed Mar  3 18:17:17 CET 2021

मैंने एक डेट-सेक रिपॉजिटरी बनाई जो इस विचार में सुधार करती है और इसे थोड़ा बेहतर बनाती है। github.com/bas080/date-seq
bas080

1

आप के साथ आ रही है बिजीबॉक्स तारीख, मैं टाइम स्टाम्प्स के साथ काम सबसे विश्वसनीय दृष्टिकोण होना पाया गया है:

STARTDATE="2019-12-30"
ENDDATE="2020-01-04"

start=$(date -d $STARTDATE +%s)
end=$(date -d $ENDDATE +%s)

d="$start"
while [[ $d -le $end ]]
do
    date -d @$d +%Y-%m-%d

    d=$(( $d + 86400 ))
done

यह आउटपुट होगा:

2019-12-30
2019-12-31
2020-01-01
2020-01-02
2020-01-03
2020-01-04

यूनिक्स टाइमस्टैम्प में लीप सेकंड शामिल नहीं हैं, इसलिए 1 दिन हमेशा 86400 सेकंड के बराबर होता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.