[bash] Bash를 사용하여 날짜를 반복하는 방법은 무엇입니까?

그런 bash 스크립트가 있습니다.

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까지)를 반복하고 싶습니다.

Bash에서 달성하는 방법?

업데이트 :

가지고 있으면 좋다 : 이전 실행이 완료되기 전에 작업을 시작해서는 안됩니다. 이 경우 executeJobs.py가 완료되면 bash 프롬프트 $가 반환됩니다.

예를 들어 wait%1내 루프에 통합 할 수 있습니까?



답변

GNU 날짜 사용 :

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

이것은 문자열 비교를 사용하기 때문에 엣지 날짜의 완전한 ISO 8601 표기법이 필요합니다 (선행 0을 제거하지 마십시오). 유효한 입력 데이터를 확인하고 가능한 경우 유효한 형식으로 강제 변환하려면 다음을 사용할 수도 있습니다 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

마지막 추가 사항 : 1000 년과 9999 년 사이의 날짜 만 예상하는 경우 $startdate이전 $enddate인지 확인하려면 다음과 같이 문자열 비교를 사용하면됩니다.

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. 아이디어는이 형식의 날짜를 숫자로 비교할 수 있다는 것입니다.


답변

중괄호 확장 :

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


답변

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


답변

나는 같은 문제가 있었고 위의 답변 중 일부를 시도했지만 괜찮을 수도 있지만 macOS를 사용하여 내가하려는 작업에 대한 답변이 수정되지 않았습니다.

나는 과거의 날짜를 반복하려고 시도했으며 다음은 나를 위해 일한 것입니다.

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

도움이되기를 바랍니다.


답변

입력 날짜에서 아래 범위로 반복하려면 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


답변

AIX, BSD, Linux, OS X 및 Solaris에서 날짜를 반복해야했습니다. 이 date명령은 내가 만난 플랫폼에서 사용하기에 가장 덜 이식 가능하고 가장 비참한 명령 중 하나입니다. 나는 쓰기가 더 쉽다는 것을 알았다my_date어디서나 작동 명령 .

아래의 C 프로그램은 시작일을 취하고 여기에서 일을 더하거나 뺍니다. 날짜가 제공되지 않으면 현재 날짜에서 일을 더하거나 뺍니다.

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

그리고 C 코드 :

#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;
}


답변

Bash는 파이프 (|)를 활용하여 작성하는 것이 가장 좋습니다. 이것은 메모리 효율적이고 동시 (더 빠른) 처리를 가져야합니다. 다음과 같이 작성합니다.

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

다음은 날짜를 20 aug 2020인쇄하고 그 전 100 일의 날짜를 인쇄합니다.

이 oneliner는 유틸리티로 만들 수 있습니다.

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

특정 날짜까지의 날짜를 생성한다고 가정 해 보겠습니다. 우리는 거기에 도달하기 위해 얼마나 많은 반복이 필요한지 아직 모릅니다. Tom이 2001 년 1 월 1 일에 태어 났다고 가정 해 보겠습니다. 특정 날짜까지 각 날짜를 생성하려고합니다. sed를 사용하여이를 달성 할 수 있습니다.

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

$((2**63-1))트릭은 큰 정수를 생성하는 데 사용됩니다.

sed가 종료되면 날짜 범위 유틸리티도 종료됩니다.

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