programing

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

lastcode 2023. 5. 7. 11:28
반응형

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에서 성취할 수 있습니까?

업데이트:

편리한 사용: 이전 실행이 완료되기 전에 작업을 시작하면 안 됩니다.Jobs 행 때 . prompt py 가 됨 료 bash$돌아올 것입니다.

예: 통합할 수 있습니까?wait%1내 루프에서?

GNU 사용 날짜:

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

  # mac option for d decl (the +1d is equivalent to + 1 day)
  # d=$(date -j -v +1d -f "%Y-%m-%d" $d +%Y-%m-%d)
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

마지막 추가 사항:확인하기$startdate 앞에 .$enddate할 수 .1000년 9999년 9999년 9999년 10월 1일자로 시작합니다.

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 숫자형로태즉즉, ,▁to,,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 -n -t"-" -k1 -k2 -k3) )
$ 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 $(date -d $start +%Y-%m-%d)
        start=$(date -d"$start + 1 day" +"%Y%m%d")

done

@Gilli의 이전 솔루션은 매우 영리합니다. 왜냐하면 그것은 두 날짜를 단순하게 포맷할 수 있기 때문입니다. 날짜를 정수처럼 보이게 만들 수 있습니다.그런 다음 일반적으로 숫자 데이터에만 사용할 수 있는 -le/덜 동등한 -을 사용할 수 있습니다.

문제는 이것이 당신을 20210201과 같은 날짜 형식의 YMD로 묶는다는 것입니다.2021-02-01(OP가 요구 사항으로 언급한 것과 동일)과 같은 다른 것이 필요한 경우 스크립트가 작동하지 않습니다.

start='2021-02-01'
end='2021-02-05'

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

출력은 다음과 같습니다.

2021-02-01
2021-02-02
2021-02-03
2021-02-04
2021-02-05
2021-02-06
2021-02-07
./loop.sh: line 16: [[: 2021-02-08: value too great for base (error token is "08")

이 문제를 해결하고 사용자 지정 날짜 형식에 이 루프를 사용하려면 "d_start"라고 하는 추가 변수를 사용해야 합니다.

d_start='2021-02-01'
end='2021-02-05'

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

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

done

이렇게 하면 다음과 같은 출력이 나옵니다.

2021-02-01
2021-02-02
2021-02-03
2021-02-04
2021-02-05

AIX, BSD, Linux, OS X 및 Solaris에서 날짜를 반복해서 확인해야 했습니다.date명령어는 제가 경험한 플랫폼에서 사용하기에 가장 휴대하기 어렵고 가장 비참한 명령어 중 하나입니다.나는 a를 쓰는 것이 더 쉽다는 것을 알았습니다.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;
}

저도 같은 문제가 있었고 위의 답변 중 몇 가지를 시도했습니다. 아마도 괜찮을지 모르지만, 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

도움이 되길 바랍니다.

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

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

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

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

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

sed가 종료되면 date-range 유틸리티도 종료됩니다.

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

도커 컨테이너에서 일반적으로 사용되는 알파인과 같은 많은 배포판에서 사용되는 바쁜 박스 날짜를 고민하고 있다면 타임스탬프 작업이 가장 신뢰할 수 있는 접근 방식이라는 것을 알게 되었습니다.

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

Unix 타임스탬프에는 윤초가 포함되지 않으므로 1일은 항상 정확히 86400초입니다.

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

이것도 도움이 될 수 있습니다.길리 답변을 기반으로 하지만 정수 변환으로 문제의 다른 해결책입니다.

기본적으로 입력 내용을 확인하는 동안LoopEachDay종료 날짜를 초 단위로 저장하고 현재 날짜를 초 단위로 처음 변환하는 것과 비교합니다(date -d "$dateIteration" '+%s')도 마찬가지입니다.

#/bin/bash

RegexVerify()
{
    regex="$1";
    shift;

    if [[ "$@" =~ $regex ]];
    then
        return 0;
    fi

    return 1;
}

VerifyDateISO8601()
{
    if RegexVerify '^[0-9]{4}-(0?[1-9]|10|11|12)-(0?[1-9]|[12][0-9]|3[01])$' "$1";
    then
        return 0;
    fi

    return 1;
}

# Iterate each day
#
# * The *first* argument is an ISO8601 start date.
# * The *second* argument is an ISO8601 end date or an empty string which assumes
# the current date.
LoopEachDay()
{
    if ! VerifyDateISO8601 "$1";
    then
        return 1;
    fi

    if ! VerifyDateISO8601 "$2" && [ "$2" != '' ];
    then
        return 2;
    fi

    dateIteration="$(date -d "$1" '+%Y-%m-%d')";
    dateIterationEndSeconds="$(date -d "$2" '+%s')";

    while (("$(date -d "$dateIteration" '+%s')" <= dateIterationEndSeconds))
    do
        printf $'%s\n' "$dateIteration"; # A work with "$dateIteration"

        dateIteration="$(date -d "$dateIteration + 1 day" '+%Y-%m-%d')";
    done
}

LoopEachDay '2021-13-01' '';
printf $'Exit code: %s\n\n' "$?";

# Exit code: 1

LoopEachDay '2021-04-01' '';

# 2021-04-01
# 2021-04-02
# 2021-04-03
# 2021-04-04
# 2021-04-05
# 2021-04-06
# 2021-04-07
# 2021-04-08

printf $'\n';
LoopEachDay '2021-04-03' '2021-04-06';

# 2021-04-03
# 2021-04-04
# 2021-04-05
# 2021-04-06

언급URL : https://stackoverflow.com/questions/28226229/how-to-loop-through-dates-using-bash

반응형