Ich habe ein solches Bash-Skript:
array=( '2015-01-01', '2015-01-02' )
for i in "${array[@]}"
do
python /home/user/executeJobs.py {i} &> /home/user/${i}.log
done
Jetzt möchte ich eine Reihe von Daten durchlaufen, z. B. 2015-01-01 bis 2015-01-31.
Wie in Bash zu erreichen?
Update :
Nice-to-have: Es sollte kein Job gestartet werden, bevor ein vorheriger Lauf abgeschlossen wurde. In diesem Fall wird nach Abschluss von executeJobs.py die Bash-Eingabeaufforderung $
zurückgegeben.
könnte ich zB wait%1
in meine Schleife aufnehmen?
datetime
Python-Modul sehr viel einfacher und zuverlässiger und portabler .wait
(wie in, Fehler aufgrund gleichzeitiger Prozesse geschehen , wenn Sie nicht tun), dann haben Sie etwas Interessanteres / kompliziertere los, was eine kompliziertere Lösung benötigt ( B. den Unterprozess zu bitten, eine Sperrdatei zu erben), was ausreichend komplex ist und nicht mit der aktuellen Arithmetik zusammenhängt, so dass es sich um eine separate Frage handeln sollte.Antworten:
Verwenden des GNU-Datums:
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
Da dies einen Zeichenfolgenvergleich verwendet, ist eine vollständige ISO 8601-Notation der Kantendaten erforderlich (führende Nullen nicht entfernen). Sie können verwenden, um nach gültigen Eingabedaten zu suchen und diese nach Möglichkeit in ein gültiges Formular zu zwingen
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
Eine letzte Ergänzung : Um dies zu überprüfen, können Sie einfach einen Zeichenfolgenvergleich wie
$startdate
folgt verwenden$enddate
, wenn Sie nur Daten zwischen den Jahren 1000 und 9999 erwarten:while [[ "$d" < "$enddate" ]]; do
Um über das Jahr 10000 hinaus auf der sicheren Seite zu sein, verwenden Sie, wenn der lexikografische Vergleich zusammenbricht
while [ "$(date -d "$d" +%Y%m%d)" -lt "$(date -d "$enddate" +%Y%m%d)" ]; do
Der Ausdruck
$(date -d "$d" +%Y%m%d)
wird$d
in eine numerische Form konvertiert , dh2015-02-23
wird20150223
, und die Idee ist, dass Daten in dieser Form numerisch verglichen werden können.quelle
%1
ist ein Jobsteuerungskonstrukt, und die Jobsteuerung ist in nicht interaktiven Skripten deaktiviert, es sei denn, Sie aktivieren sie explizit selbst. Der richtige Weg, um auf einzelne Unterprozesse innerhalb eines Skripts zu verweisen, ist die PID. Selbst dann erfolgt das Warten auf den Abschluss von Prozessen automatisch, es sei denn, sie werden explizit von Ihrem Code (wie bei a&
) hinterlegt oder lösen sich selbst (in diesem Fall)wait
funktioniert nicht einmal und die der Shell zugewiesene PID wird durch den Double-Fork-Prozess ungültig, der für den Selbsthintergrund verwendet wird.Klammererweiterung :
for i in 2015-01-{01..31} …
Mehr:
for i in 2015-02-{01..28} 2015-{04,06,09,11}-{01..30} 2015-{01,03,05,07,08,10,12}-{01..31} …
Beweis:
$ 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
Kompakt / verschachtelt:
$ echo 2015-{02-{01..28},{04,06,09,11}-{01..30},{01,03,05,07,08,10,12}-{01..31}} | wc -w 365
Bestellt, wenn es darauf ankommt:
$ 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
Da es ungeordnet ist, können Sie einfach Schaltjahre später angehen:
$ 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
quelle
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
quelle
Ich hatte das gleiche Problem und habe einige der oben genannten Antworten ausprobiert. Vielleicht sind sie in Ordnung, aber keine dieser Antworten hat sich auf das konzentriert, was ich mit macOS versucht habe.
Ich habe in der Vergangenheit versucht, Daten zu durchlaufen, und Folgendes hat bei mir funktioniert:
#!/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
Ich hoffe es hilft.
quelle
gdate
anstelle vondate
in macOS.Wenn eine Schleife vom Eingabedatum bis zu einem der folgenden Bereiche verwendet werden soll, wird auch die Ausgabe im Format JJJJMMTT ...
#!/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
quelle
Ich musste Daten unter AIX, BSDs, Linux, OS X und Solaris durchlaufen. Der
date
Befehl ist einer der am wenigsten portablen und miserabelsten Befehle, die plattformübergreifend verwendet werden können. Ich fand es einfacher, eine zu schreibenmy_date
Befehl , der einfach überall funktionierte.Das folgende C-Programm nimmt ein Startdatum und addiert oder subtrahiert Tage davon. Wenn kein Datum angegeben wird, werden Tage zum aktuellen Datum addiert oder subtrahiert.
Mit dem
my_date
Befehl können Sie überall Folgendes ausführen: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
Und der 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; }
quelle
Bash wird am besten durch Nutzung von Rohren (|) geschrieben. Dies sollte zu einer speichereffizienten und gleichzeitigen (schnelleren) Verarbeitung führen. Ich würde folgendes schreiben:
seq 0 100 | xargs printf "20 Aug 2020 - %sdays\n" \ | xargs -d '\n' -l date -d
Im Folgenden wird das Datum
20 aug 2020
und die Daten der 100 Tage davor gedruckt.Dieser Oneliner kann zu einem Dienstprogramm gemacht werden.
#!/usr/bin/env bash # date-range template <template> template="${1:--%sdays}" export LANG; xargs printf "$template\n" | xargs -d '\n' -l date -d
Standardmäßig iterieren wir jeweils in den letzten 1 Tag.
Angenommen, wir möchten Daten bis zu einem bestimmten Datum generieren. Wir wissen noch nicht, wie viele Iterationen wir benötigen, um dorthin zu gelangen. Angenommen, Tom wurde am 1. Januar 2001 geboren. Wir möchten jedes Datum bis zu einem bestimmten Datum generieren. Wir können dies erreichen, indem wir sed verwenden.
seq 0 $((2**63-1)) | date-range | sed '/.. Jan 2001 /q'
Nach dem Beenden von sed wird auch das Dienstprogramm für den Datumsbereich beendet.
Man kann auch mit einem Intervall von 3 Monaten iterieren:
$ 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
quelle
Wenn Sie mit dem Datum der Busybox nicht weiterkommen , hat sich die Arbeit mit Zeitstempeln als der zuverlässigste Ansatz herausgestellt:
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
Dies wird Folgendes ausgeben:
Der Unix-Zeitstempel enthält keine Schaltsekunden, sodass 1 Tag immer genau 86400 Sekunden entspricht.
quelle