How to loop through dates using Bash?

后端 未结 8 714
一整个雨季
一整个雨季 2020-12-02 06:22

I have such bash script:

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

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


        
相关标签:
8条回答
  • 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
    
    0 讨论(0)
  • 2020-12-02 06:30

    If you're stuck with busybox date, I've found working with timestamps to be the most reliable approach:

    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
    

    This will output:

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

    Unix timestamp don't include leap seconds, so 1 day equals always exactly 86400 seconds.

    0 讨论(0)
  • 2020-12-02 06:37

    I needed to loop through dates on AIX, BSDs, Linux, OS X and Solaris. The date command is one of the least portable and most miserable commands to use across platforms I have encountered. I found it easier to write a my_date command that just worked everywhere.

    The C program below takes a starting date, and adds or subtracts days from it. If no date is supplied, it adds or subtracts days from the current date.

    The my_date command allows you to perform the following everywhere:

    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
    

    And the C code:

    #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;
    }
    
    0 讨论(0)
  • 2020-12-02 06:39

    Using GNU date:

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

    Note that because this uses string comparison, it requires full ISO 8601 notation of the edge dates (do not remove leading zeros). To check for valid input data and coerce it to a valid form if possible, you can use date as well:

    # 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
    

    One final addition: To check that $startdate is before $enddate, if you only expect dates between the years 1000 and 9999, you can simply use string comparison like this:

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

    To be on the very safe side beyond the year 10000, when lexicographical comparison breaks down, use

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

    The expression $(date -d "$d" +%Y%m%d) converts $d to a numerical form, i.e., 2015-02-23 becomes 20150223, and the idea is that dates in this form can be compared numerically.

    0 讨论(0)
  • 2020-12-02 06:46

    Bash is best written by leveraging pipes(|). This should result in memory efficient and concurrent(faster) processing. I would write the following:

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

    The following will print the date of 20 aug 2020 and print the dates of the 100 days before it.

    This oneliner can be made into a utility.

    #!/usr/bin/env bash
    
    # date-range template <template>
    
    template="${1:--%sdays}"
    
    export LANG;
    
    xargs printf "$template\n" | xargs -d '\n' -l date -d
    

    By default we choose to iterate into the past 1 day at a time.

    $ 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
    

    Let's say we want to generate dates up to a certain date. We don't know yet how many iterations we need to get there. Let's say Tom was born 1 Jan 2001. We want to generate each date till a certain one. We can achieve this by using sed.

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

    The $((2**63-1)) trick is used to create a big integer.

    Once sed exits it will also exit the date-range utility.

    One can also iterate using a 3 month interval:

    $ 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
    
    0 讨论(0)
  • 2020-12-02 06:48

    If one wants to loop from input date to any range below can be used, also it will print output in format of yyyyMMdd...

    #!/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
    
    0 讨论(0)
提交回复
热议问题