У меня есть такой скрипт на bash:
array=( '2015-01-01', '2015-01-02' )
for i in "${array[@]}"
do
python /home/user/executeJobs.py {i} &> /home/user/${i}.log
done
Теперь я хочу перебрать диапазон дат, например с 01.01.2015 по 31.01.2015.
Как добиться в Баше?
Обновление :
Приятно иметь: никакие задания не должны начинаться до завершения предыдущего запуска. В этом случае, когда executeJobs.py завершится, $
появится приглашение bash .
например, могу ли я включить wait%1
в свой цикл?
datetime
модуля Python.wait
(как, ошибки происходит из - за параллельные процессы , когда вы не), то у вас есть что - то более интересное / более осложненный происходит, что требует более сложного решения ( например, попросить подпроцесс унаследовать файл блокировки), что достаточно сложно и не имеет отношения к арифметике дат, поэтому это должен быть отдельный вопрос.Ответы:
Используя дату 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" "2020-12-12" +%Y-%m-%d) done
Обратите внимание, что, поскольку здесь используется сравнение строк, требуется полная запись крайних дат в соответствии с ISO 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
это конструкция управления заданиями, и управление заданиями отключено в неинтерактивных скриптах, если вы явно не включите его сами. Правильный способ ссылки на отдельные подпроцессы внутри сценария - это PID, и даже в этом случае ожидание завершения процессов происходит автоматически, если только они явно не основаны на вашем коде (как с a&
) или не отключаются самостоятельно (в этом случаеwait
даже не будет работать, и PID, предоставленный оболочке, будет аннулирован процессом двойной вилки, используемым для самофонового режима).Расширение скобок :
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
источник
python /home/user/executeJobs.py 2015-01-{01..31} &> /home/user/2015-01-{01..31}.log
?executeJobs.py
.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
Надеюсь, поможет.
источник
gdate
вместоdate
macOS.Если кто-то хочет перейти от даты ввода к любому диапазону, указанному ниже, он также будет печатать вывод в формате 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 дней до нее.Этот одиночный лайнер можно превратить в служебную программу.
#!/usr/bin/env bash # date-range template <template> template="${1:--%sdays}" export LANG; xargs printf "$template\n" | xargs -d '\n' -l date -d
По умолчанию мы выбираем итерацию в последний день за раз.
Допустим, мы хотим сгенерировать даты до определенной даты. Мы еще не знаем, сколько итераций нам нужно для этого. Допустим, Том родился 1 января 2001 года. Мы хотим генерировать каждую дату до определенной. Этого можно добиться с помощью sed.
seq 0 $((2**63-1)) | date-range | sed '/.. Jan 2001 /q'
После выхода 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
источник
Если вы застряли с датой busybox , я считаю, что работа с метками времени является наиболее надежным подходом:
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
Это выведет:
Метка времени Unix не включает дополнительные секунды, поэтому 1 день всегда равен 86400 секундам.
источник