Tengo tal script bash:
array=( '2015-01-01', '2015-01-02' )
for i in "${array[@]}"
do
python /home/user/executeJobs.py {i} &> /home/user/${i}.log
done
Ahora quiero recorrer un rango de fechas, por ejemplo, 2015-01-01 hasta 2015-01-31.
¿Cómo lograrlo en Bash?
Actualización :
Es bueno tenerlo: ningún trabajo debe iniciarse antes de que se haya completado una ejecución anterior. En este caso, cuando se complete executeJobs.py, el indicador de bash $
volverá.
por ejemplo, ¿podría incorporar wait%1
en mi bucle?
datetime
módulo de Python.wait
(como en, errores que suceden debido a los procesos concurrentes cuando no lo hace), entonces usted tiene algo más interesante / complicado ir más adelante, que necesita una solución más complicada ( como pedirle al subproceso que herede un archivo de bloqueo), que es lo suficientemente complejo y no está lo suficientemente relacionado con la aritmética de la fecha como para que sea una pregunta separada.Respuestas:
Usando la fecha GNU:
d=2015-01-01 while [ "$d" != 2015-02-20 ]; do echo $d d=$(date -I -d "$d + 1 day") done
Tenga en cuenta que debido a que utiliza la comparación de cadenas, requiere la notación ISO 8601 completa de las fechas del borde (no elimine los ceros iniciales). Para verificar datos de entrada válidos y convertirlos en un formulario válido si es posible, también puede usar
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
Una última adición : para verificar eso
$startdate
es antes$enddate
, si solo espera fechas entre los años 1000 y 9999, simplemente puede usar una comparación de cadenas como esta:while [[ "$d" < "$enddate" ]]; do
Para estar muy seguro más allá del año 10000, cuando se rompa la comparación lexicográfica, utilice
while [ "$(date -d "$d" +%Y%m%d)" -lt "$(date -d "$enddate" +%Y%m%d)" ]; do
La expresión se
$(date -d "$d" +%Y%m%d)
convierte$d
a una forma numérica, es decir, se2015-02-23
convierte20150223
, y la idea es que las fechas en esta forma se pueden comparar numéricamente.fuente
%1
es una construcción de control de trabajos, y el control de trabajos está desactivado en scripts no interactivos a menos que lo active explícitamente usted mismo. La forma correcta de referirse a subprocesos individuales dentro de un script es mediante PID, e incluso entonces, esperar a que los procesos se completen es automático, a menos que estén explícitamente respaldados por su código (como con a&
), o se separen automáticamente (en cuyo casowait
ni siquiera funcionará, y el PID dado al shell será invalidado por el proceso de doble bifurcación utilizado para el auto-fondo).Expansión de la abrazadera :
for i in 2015-01-{01..31} …
Más:
for i in 2015-02-{01..28} 2015-{04,06,09,11}-{01..30} 2015-{01,03,05,07,08,10,12}-{01..31} …
Prueba:
$ 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
Compacto / anidado:
$ echo 2015-{02-{01..28},{04,06,09,11}-{01..30},{01,03,05,07,08,10,12}-{01..31}} | wc -w 365
Ordenado, si importa:
$ 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
Dado que no está ordenado, puede seguir los años bisiestos:
$ 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
fuente
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
fuente
Tuve el mismo problema y probé algunas de las respuestas anteriores, tal vez estén bien, pero ninguna de esas respuestas se solucionó en lo que estaba tratando de hacer, usando macOS.
Estaba tratando de iterar sobre fechas en el pasado, y lo siguiente es lo que funcionó para mí:
#!/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
Espero eso ayude.
fuente
gdate
lugar dedate
en macOS.Si uno quiere recorrer desde la fecha de entrada hasta cualquier rango a continuación, se puede usar, también imprimirá la salida en formato de aaaaMMdd ...
#!/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
fuente
Necesitaba recorrer las fechas en AIX, BSD, Linux, OS X y Solaris. El
date
comando es uno de los comandos menos portátiles y más miserables para usar en todas las plataformas que he encontrado. Me resultó más fácil escribir unmy_date
comando que simplemente funcionaba en todas partes.El programa C a continuación toma una fecha de inicio y agrega o resta días de ella. Si no se proporciona una fecha, suma o resta días de la fecha actual.
El
my_date
comando le permite realizar lo siguiente en todas partes: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
Y el código 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; }
fuente
Bash se escribe mejor aprovechando las tuberías (|). Esto debería resultar en un procesamiento concurrente (más rápido) y eficiente en la memoria. Escribiría lo siguiente:
seq 0 100 | xargs printf "20 Aug 2020 - %sdays\n" \ | xargs -d '\n' -l date -d
Lo siguiente imprimirá la fecha
20 aug 2020
y las fechas de los 100 días anteriores.Este delineador puede convertirse en una utilidad.
#!/usr/bin/env bash # date-range template <template> template="${1:--%sdays}" export LANG; xargs printf "$template\n" | xargs -d '\n' -l date -d
De forma predeterminada, elegimos iterar en el último día a la vez.
Digamos que queremos generar fechas hasta una fecha determinada. Aún no sabemos cuántas iteraciones necesitamos para llegar allí. Digamos que Tom nació el 1 de enero de 2001. Queremos generar cada fecha hasta una determinada. Podemos lograr esto usando sed.
seq 0 $((2**63-1)) | date-range | sed '/.. Jan 2001 /q'
Una vez que sed sale, también saldrá de la utilidad de rango de fechas.
También se puede iterar usando un intervalo de 3 meses:
$ 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
fuente
Si está atascado con la fecha de busybox , he descubierto que trabajar con marcas de tiempo es el enfoque más confiable:
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
Esto dará como resultado:
La marca de tiempo de Unix no incluye segundos intercalares, por lo que 1 día es siempre exactamente 86400 segundos.
fuente