
Tengo esta entrada:
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
en el que una línea especifica un rango de fechas que abarca varios días, y quiero dividir el rango en períodos de tiempo separados, siendo cada uno un subconjunto de un día (cada uno en una línea separada), para facilitar el procesamiento paralelo de (multi -días) rango.
La salida debe ser
2015-10-13 07:00:02 2015-10-13 23:59:59 45 1900
2015-10-14 00:00:01 2015-10-14 23:59:59 45 1900
2015-10-15 00:00:01 2015-10-15 23:59:59 45 1900
2015-10-16 00:00:01 2015-10-16 23:59:59 45 1900
2015-10-17 00:00:01 2015-10-17 23:59:59 45 1900
2015-10-18 00:00:01 2015-10-18 23:59:59 45 1900
2015-10-19 00:00:01 2015-10-19 00:00:00 45 1900
donde los datos después de la hora de finalización (val1 y val2) se replican en cada línea.
- En realidad, los registros de entrada provienen de la tabla de la colmena y los registros de salida también los almacenarán en la tabla dividida.
Modificaciones:
la división de fechas está bien. Es necesario dividir el valor val2 también según la fecha de división.
si la diferencia de fecha es 2, entonces dividiríamos 2 filas que deberían ser
- fila 1:
ratio = ratio de tiempo empleado el primer día (es decir, final-inicio en el día 1) /valor1
val2= relación*val2
- fila 2:
ratio = ratio de tiempo empleado el primer día (es decir, final-comienzo en el día 2) /valor1
valor2= relación*val2
¿Cómo puedo escribir esto?
Respuesta1
Este script hará lo que usted quiera (si entiendo sus requisitos correctamente). Me tomé la libertad de extrapolar su especificación para permitir que la entrada tenga una línea de encabezado y luego cualquier cantidad de líneas con rangos de fecha/hora. Ilustraré esto y lo discutiré más a continuación.
#!/bin/sh
if IFS= read header
then
printf "%s\n" "$header"
else
echo 'EOF on first line!' >&2
exit 1
fi
while read start_date start_time end_date end_time other_data # See note, below.
do
start_epoch=$(date +"%s" -d "$start_date $start_time") || {
echo "Error processing start date&time $start_date $start_time" >&2
exit 1
}
end_epoch=$(date +"%s" -d "$end_date $end_time") || {
echo "Error processing end date&time $end_date $end_time" >&2
exit 1
}
if [ "$end_epoch" -lt "$start_epoch" ]
then
echo "End date&time $end_date $end_time is before start date&time $start_date $start_time" >&2
# Now what?
continue
fi
ok_seq=1 # Flag: we are moving forward.
current_date="$start_date"
current_time="$start_time"
while [ "$ok_seq" -ne 0 ]
do
# Most days end at 23:59:59.
eod_time="23:59:59"
eod_epoch=$(date +"%s" -d "$current_date $eod_time") || {
# This should never happen.
echo "Error processing end-of-day date&time $current_date $eod_time" >&2
exit 1
}
if [ "$end_epoch" -lt "$eod_epoch" ] # We’re passing the end of the date/time range.
then
if [ "$current_date" != "$end_date" ]
then
# Sanity check -- this should not happen.
echo "We're finishing, but the current date is $current_date and the end date is $end_date" >&2
fi
eod_time="$end_time"
ok_seq=0
fi
# See note, below.
printf "%s %s %s %s %s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"
# We could also use +"%F" for the full YYYY-mm-dd date.
current_date=$(date +"%Y-%m-%d" -d "$current_date next day") || {
# This shouldn’t happen.
echo "Error getting next day after $current_date" >&2
exit 1
}
current_time="00:00:01"
done
done
Discusión:
- Lea la línea del encabezado. Si esto falla, cancele el script. Si tiene éxito, escriba la línea en la salida. Si (como muestra su pregunta) no desea que el encabezado aparezca en su salida, elimine la
printf "%s\n" "$header"
declaración. - Como se mencionó anteriormente: bucle, leyendo las líneas de inicio/fin/valor desde la entrada hasta que llegamos al final de la entrada (o obtenemos un error fatal). Si no desea hacer esto, elimine el
while
, eldo
y el correspondientedone
. - Lea la fecha de inicio, hora de inicio, fecha de finalización, hora de finalización y otros datos.
other_data
incluye todo después de la hora de finalización, es decir, val1 y val2 (y todo el espacio entre ellos). - Utilice el comando para convertir cadenas de fecha y hora arbitrarias a “tiempos de época” de Unix: el número de segundos desde 1970-01-01 00:00:00 (GMT). Esto nos permite validar la entrada (y salir en caso de error) y también nos proporciona números que podemos comparar. (Aunque supongo que podríamos simplemente hacer una comparación de cadenas en valores formateados como AAAA-MM-DD HH:MM:SS).
date +"%s" -d "date/time string"
- Si la fecha/hora de finalización es anterior a la fecha/hora de inicio, omita este registro y pase a la siguiente línea. Si prefiere hacer otra cosa (como terminar) en este caso, cambie este código.
- Establezca una bandera (
ok_seq
) que usaremos para controlar el ciclo que recorre los días. Inicialice la fecha/hora de inicio del primer día para que sea la fecha/hora de inicio de todo el período. - En cada línea de salida, la fecha de inicio y la fecha de finalización son las mismas. En la mayoría de las líneas, la hora de fin del día (eod) es las 23:59:59. Si (misma fecha) + 23:59:59 es mayor (más tarde) que la fecha/hora de fin del período, entonces estamos en el último día (línea de salida) del rango. Establezca el tiempo de eod en la hora de finalización y configúrelo
ok_seq
en 0 para salir del ciclo. - Escriba una línea de salida, incluidos los "otros datos" (val1 y val2, etc.)
- Calcule la fecha del día siguiente. Establezca la hora de inicio en 00:00:01, que aparecerá en cada línea de salida excepto en la primera.
Ejemplo:
$ cat input
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
2015-11-01 08:30:00 2015-11-05 15:00:00 42 6083
2015-12-27 12:00:00 2016-01-04 12:34:56 17 quux
$ ./script < input
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-13 23:59:59 45 1900
2015-10-14 00:00:01 2015-10-14 23:59:59 45 1900
2015-10-15 00:00:01 2015-10-15 23:59:59 45 1900
2015-10-16 00:00:01 2015-10-16 23:59:59 45 1900
2015-10-17 00:00:01 2015-10-17 23:59:59 45 1900
2015-10-18 00:00:01 2015-10-18 23:59:59 45 1900
2015-10-19 00:00:01 2015-10-19 00:00:00 45 1900
2015-11-01 08:30:00 2015-11-01 23:59:59 42 6083
2015-11-02 00:00:01 2015-11-02 23:59:59 42 6083
2015-11-03 00:00:01 2015-11-03 23:59:59 42 6083
2015-11-04 00:00:01 2015-11-04 23:59:59 42 6083
2015-11-05 00:00:01 2015-11-05 15:00:00 42 6083
2015-12-27 12:00:00 2015-12-27 23:59:59 17 quux
2015-12-28 00:00:01 2015-12-28 23:59:59 17 quux
2015-12-29 00:00:01 2015-12-29 23:59:59 17 quux
2015-12-30 00:00:01 2015-12-30 23:59:59 17 quux
2015-12-31 00:00:01 2015-12-31 23:59:59 17 quux
2016-01-01 00:00:01 2016-01-01 23:59:59 17 quux
2016-01-02 00:00:01 2016-01-02 23:59:59 17 quux
2016-01-03 00:00:01 2016-01-03 23:59:59 17 quux
2016-01-04 00:00:01 2016-01-04 12:34:56 17 quux
Observa que no tiene problemas para renovarse, no sólo de un mes a otro, sino también de un año al siguiente.
Nota: Cuando escribí la versión anterior del script, no pude entender cómo capturar el espacio en blanco entre la hora de finalización y val1, por lo que obtuve un resultado que se parecía a
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-13 23:59:59 45 1900
2015-10-14 00:00:01 2015-10-14 23:59:59 45 1900
2015-10-15 00:00:01 2015-10-15 23:59:59 45 1900
︙
así que hice "trampa" al crear la 'cantidad correcta' de espacio en el printf
comando (antes del último %s
). Pero si cambia el espaciado en su entrada, la versión anterior del script volverá a producir columnas incorrectamente alineadas. Descubrí cómo solucionarlo, aunque es un poco complicado. Reemplace las líneas while …
do
… start_epoch=…
con:
while read start_date start_time end_date other_data
do
# $other_data includes end_time and all the following values.
# Break them apart:
end_time="${other_data%%[ ]*}"
other_data="${other_data#"$end_time"}"
start_epoch=…
donde end_time
se ha eliminado del read
comando, y los caracteres entre corchetes [
y ]
son un espacio y una tabulación. Ahora other_data
contiene los espacios antes de val1. Luego cambie el printf
a
printf "%s %s %s %s%s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"
(tenga en cuenta que hayNoespacio entre el cuarto y el quinto %s
). Así que ya terminaste.
Respuesta2
Supongo que estás buscando deshacerte de la línea del encabezado superior. Digamos que la función de la que obtiene esta entrada se llama 'timefunc'. Quizás quieras intentar canalizar la salida de timefunc en un comando de corte como este:
timefunc | cut -d$'\n' -f2
La salida ahora es:
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
Respuesta3
puedes eliminar las líneas de encabezado de tu salida con grep:
inputcmd | grep -v startdate