Mam taki skrypt basha:
array=( '2015-01-01', '2015-01-02' )
for i in "${array[@]}"
do
python /home/user/executeJobs.py {i} &> /home/user/${i}.log
done
Teraz chcę przeglądać zakres dat, np. 01.01.2015 do 31.01.2015.
Jak osiągnąć w Bash?
Aktualizacja :
Przyjemne do posiadania: żadne zadanie nie powinno być uruchamiane przed zakończeniem poprzedniego uruchomienia. W tym przypadku, po zakończeniu executeJobs.py bash $
zwróci znak zachęty .
np. czy mogę włączyć wait%1
do mojej pętli?
datetime
modułu Python.wait
(jak w, błędy zachodzące w wyniku procesów współbieżnych gdy nie robić), to trzeba coś bardziej interesującego / bardziej skomplikowane dzieje, którego potrzebuje bardziej skomplikowane rozwiązanie ( jak poproszenie podprocesu o odziedziczenie pliku blokującego), co jest na tyle skomplikowane i wystarczająco niezwiązane z arytmetyką datowania, że powinno to być oddzielne pytanie.Odpowiedzi:
Korzystanie z daty GNU:
d=2015-01-01 while [ "$d" != 2015-02-20 ]; do echo $d d=$(date -I -d "$d + 1 day") done
Zauważ, że ponieważ używa to porównania ciągów, wymaga pełnej notacji ISO 8601 dla dat krawędzi (nie usuwaj wiodących zer). Aby sprawdzić poprawne dane wejściowe i przekształcić je w prawidłowy formularz, jeśli to możliwe, możesz również użyć
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
Ostatni dodatek : aby to sprawdzić
$startdate
wcześniej$enddate
, jeśli spodziewasz się tylko dat z przedziału od 1000 do 9999, możesz po prostu użyć porównania ciągów w następujący sposób:while [[ "$d" < "$enddate" ]]; do
Aby być po bardzo bezpiecznej stronie po roku 10000, gdy porównanie leksykograficzne załamie się, użyj
while [ "$(date -d "$d" +%Y%m%d)" -lt "$(date -d "$enddate" +%Y%m%d)" ]; do
Wyrażenie jest
$(date -d "$d" +%Y%m%d)
konwertowane$d
do postaci liczbowej, tj.2015-02-23
Staje się20150223
, a idea polega na tym, że daty w tej formie można porównywać liczbowo.źródło
%1
jest konstrukcją kontroli zadań, a kontrola zadań jest wyłączona w nieinteraktywnych skryptach, chyba że wyraźnie ją włączysz. Właściwym sposobem odwoływania się do poszczególnych podprocesów wewnątrz skryptu jest PID, a nawet wtedy oczekiwanie na zakończenie procesów jest automatyczne, chyba że są one jawnie umieszczone w tle przez Twój kod (jak w przypadku a&
) lub same się odłączają (w takim przypadkuwait
nawet nie zadziała, a PID podany powłoce zostanie unieważniony przez proces podwójnego rozwidlenia używany do samodzielnego tła).Rozszerzenie nawiasów :
for i in 2015-01-{01..31} …
Jeszcze:
for i in 2015-02-{01..28} 2015-{04,06,09,11}-{01..30} 2015-{01,03,05,07,08,10,12}-{01..31} …
Dowód:
$ 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
Kompaktowy / zagnieżdżony:
$ echo 2015-{02-{01..28},{04,06,09,11}-{01..30},{01,03,05,07,08,10,12}-{01..31}} | wc -w 365
Zamówione, jeśli ma to znaczenie:
$ 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
Ponieważ jest nieuporządkowany, możesz po prostu ustawić lata przestępne na:
$ 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
źródło
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
źródło
Miałem ten sam problem i wypróbowałem niektóre z powyższych odpowiedzi, może są w porządku, ale żadna z tych odpowiedzi nie rozwiązała tego, co próbowałem zrobić, używając macOS.
Próbowałem iterować daty w przeszłości i następujące rzeczy zadziałały dla mnie:
#!/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
Mam nadzieję, że to pomoże.
źródło
gdate
zamiastdate
macOS.Jeśli ktoś chce wykonać pętlę od daty wejściowej do dowolnego zakresu poniżej, może również wydrukować dane wyjściowe w formacie rrrrMMdd ...
#!/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
źródło
Musiałem przeglądać daty w systemach AIX, BSD, Linux, OS X i Solaris.
date
Komenda jest jedną z najmniej przenośnych i najbardziej nędznych komend do wykorzystania na różnych platformach, jakie napotykają. Łatwiej było napisać plikmy_date
polecenie, które działało wszędzie.Poniższy program w języku C przyjmuje datę początkową i dodaje lub odejmuje od niej dni. Jeśli nie podano daty, dodaje lub odejmuje dni od bieżącej daty.
my_date
Komenda pozwala na wykonywanie następujących wszędzie: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
A kod 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; }
źródło
Bash najlepiej pisać przy użyciu potoków (|). Powinno to skutkować wydajnym i współbieżnym (szybszym) przetwarzaniem pamięci. Napisałbym co następuje:
seq 0 100 | xargs printf "20 Aug 2020 - %sdays\n" \ | xargs -d '\n' -l date -d
Poniżej zostanie wydrukowana data
20 aug 2020
i data sprzed 100 dni.Ten oneliner można przekształcić w narzędzie.
#!/usr/bin/env bash # date-range template <template> template="${1:--%sdays}" export LANG; xargs printf "$template\n" | xargs -d '\n' -l date -d
Domyślnie wybieramy iterację w ciągu ostatniego 1 dnia na raz.
Powiedzmy, że chcemy wygenerować daty do określonej daty. Nie wiemy jeszcze, ile iteracji potrzebujemy, aby się tam dostać. Powiedzmy, że Tomek urodził się 1 stycznia 2001 roku. Chcemy wygenerować każdą datę do pewnej. Możemy to osiągnąć używając seda.
seq 0 $((2**63-1)) | date-range | sed '/.. Jan 2001 /q'
Po zakończeniu sed zamknie również narzędzie zakresu dat.
Można również iterować stosując interwał 3-miesięczny:
$ 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
źródło
Jeśli utkniesz z datą zajętości , uważam, że praca ze znacznikami czasu jest najbardziej niezawodnym podejściem:
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
Spowoduje to wyświetlenie:
Uniksowy znacznik czasu nie zawiera sekund przestępnych, więc 1 dzień to zawsze dokładnie 86400 sekund.
źródło