Este script fará o que você quiser (se eu entendi seus requisitos corretamente). Tomei a liberdade de extrapolar sua especificação para permitir que a entrada tenha uma linha de cabeçalho e, em seguida, qualquer número de linhas com intervalos de data / hora. Vou ilustrar isso e discuti-lo mais adiante.
#!/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
Discussão:
- Leia a linha do cabeçalho.
Se isso falhar, aborte o script.
Se for bem sucedido, escreva a linha na saída.
Se (como sua pergunta mostra) você não quer o cabeçalho na sua saída,
remova a instrução
printf "%s\n" "$header"
. - Como mencionado acima:
loop, lendo linhas de início / fim / valor da entrada
até chegarmos ao final da entrada (ou obter um erro fatal).
Se você não quiser fazer isso,
remova o
while
, odo
e o correspondentedone
. - Leia a data de início, a hora de início, a data de término, a hora de término e outros dados.
other_data
inclui tudo após o horário de término ou seja, val1 e val2 (e todo o espaço entre eles). - Use o comando
date +"%s" -d "date/time string"
converter strings de data / hora arbitrárias em Unix "epoch times" - o número de segundos desde 1970-01-01 00:00:00 (GMT). Isso nos permite validar a entrada (e sair em caso de erro), e também nos dá números que podemos comparar. (Embora eu suponha que pudéssemos fazer uma comparação de string em valores formatados como AAAA-MM-DD HH: MM: SS.) - Se a data / hora final for anterior à data / hora de início, pule este registro e vá para a próxima linha. Se você preferir fazer outra coisa (como terminar) nesse caso, altere este código.
- Definir um sinalizador (
ok_seq
) que usaremos para controlar o loop que percorre os dias. Inicialize a data / hora de início do primeiro dia para ser a data / hora de início para todo o período. - Em cada linha de saída, a data de início e a data final são as mesmas.
Na maioria das linhas, o final do dia (eod) é 23:59:59.
Se (mesma data) + 23:59:59 for maior (mais tarde) que
a data / hora de fim de período,
então estamos no último dia (linha de saída) do intervalo.
Defina o tempo eod para o tempo final,
e defina
ok_seq
para 0, então vamos sair do loop. - Escreva uma linha de saída, incluindo os "outros dados" (val1 e val2, etc.)
- Calcule a data do dia seguinte. Defina a hora de início para 00:00:01, que aparecerá em cada linha de saída, exceto pela primeira.
Exemplo:
$ 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
Observe que não há problema em rolar não só de um mês para o outro, mas também de um ano para o outro.
Nota: Quando eu escrevi a versão acima do script, Eu não consegui descobrir como capturar o espaço em branco entre o tempo final e val1, então eu estava recebendo saída que parecia
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
︙
então eu "enganei",
construindo a "quantidade certa" de espaço no comando printf
(antes do último %s
).
Mas se você alterar o espaçamento na sua entrada,
a versão acima do script será novamente
produzir colunas alinhadas incorretamente.
Eu descobri como consertar isso, embora seja um pouco confuso.
Substitua as linhas while …
do
… start_epoch=…
por:
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=…
em que end_time
foi removido do comando read
,
e os caracteres entre os parênteses [
e ]
são um espaço e uma aba.
Então agora other_data
contém os espaços antes de val1.
Em seguida, altere o printf
para
printf "%s %s %s %s%s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"
(note que não há nenhum espaço entre o quarto e quinto %s
).
Então agora você está feito.