Como dividir o intervalo de datas em dias usando script

Como dividir o intervalo de datas em dias usando script

Eu tenho esta entrada:

      startdate             end date         val1    val2
2015-10-13 07:00:02 2015-10-19 00:00:00      45      1900

em que uma linha especifica um intervalo de datas que abrange vários dias, e quero dividir o intervalo em períodos de tempo separados, cada um sendo um subconjunto de um dia (cada um em uma linha separada), para facilitar o processamento paralelo do (multi -dia).

A saída deve 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

onde os dados após o horário de término (val1 e val2) são replicados em cada linha. 

  1. Na verdade, os registros de entrada vêm da tabela Hive e os registros de saída também irão armazená-los na tabela dividida.

Modificações:

divisão de data está bem. precisa dividir o valor val2 também de acordo com a data de divisão.

se a diferença de data for 2, dividiríamos 2 linhas que deveriam ser

  • linha 1:

proporção = proporção do tempo gasto no 1º dia (ou seja, final-início no dia 1) /val1

val2= razão*val2

  • linha 2:

proporção = proporção do tempo gasto no 1º dia (ou seja, final-início no dia 2) /val1

val2= razão*val2

Como posso criar um script para isso?

Responder1

Este script fará o que você deseja (se eu entender 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 detalhadamente abaixo.

#!/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, interrompa o script. Se tiver sucesso, escreva a linha na saída. Se (como mostra sua pergunta) você não deseja o cabeçalho em sua saída, remova a printf "%s\n" "$header"instrução.
  • 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, o doe o correspondente done.
  • Leia a data de início, hora de início, data de término, hora de término e outros dados.  other_datainclui tudo após o horário de término, ou seja, val1 e val2 (e todo o espaço entre eles).
  • Use o comando para converter strings arbitrárias de data/hora em “tempos de época” Unix – o número de segundos desde 01/01/1970 00:00:00 (GMT). Isso nos permite validar a entrada (e sair em caso de erro) e também nos fornece números que podemos comparar. (Embora eu suponha que poderíamos apenas fazer uma comparação de strings em valores formatados como AAAA-MM-DD HH:MM:SS.)date +"%s" -d "date/time string"
  • Se a data/hora de término for anterior à data/hora de início, pule este registro e vá para a próxima linha. Se você preferir fazer outra coisa (como encerrar) neste caso, altere este código.
  • Defina 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 de todo o período.
  • Em cada linha de saída, a data de início e a data de término são iguais. Na maioria das linhas, o horário de fim do dia (eod) é 23:59:59. Se (mesma data) + 23:59:59 for maior (mais tarde) que a data/hora de final do período, então estamos no último dia (linha de saída) do intervalo. Defina o tempo eod como o horário de término e defina ok_seqcomo 0 para sairmos 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 na 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 tem problema em rolar, não só de um mês para o outro, mas também de um ano para o outro.


Observação: Quando escrevi a versão acima do script, não consegui descobrir como capturar o espaço em branco entre o horário de término e val1, então estava obtendo uma 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 “trapaceei”, construindo a 'quantidade certa' de espaço no printfcomando (antes do último %s). Mas se você alterar o espaçamento na sua entrada, a versão acima do script produzirá novamente colunas alinhadas incorretamente. Eu descobri como consertar isso, embora esteja um pouco confuso. Substitua as while … dostart_epoch=…linhas 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=…

onde end_timefoi removido do readcomando e os caracteres entre colchetes [e ] são um espaço e uma tabulação. Então agora other_datacontém os espaços antes de val1. Então mude printfpara

                printf "%s %s %s %s%s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"

(observe que hánãoespaço entre o quarto e o quinto %s). Então agora você terminou.

Responder2

Suponho que você esteja tentando se livrar da linha do cabeçalho superior. Digamos que a função da qual você está obtendo essa entrada seja chamada de 'timefunc'. Você pode tentar canalizar a saída do timefunc em um comando cut como este:

timefunc | cut -d$'\n' -f2

A saída é agora:

2015-10-13 07:00:02 2015-10-19 00:00:00      45      1900

Responder3

você pode retirar as linhas de cabeçalho da sua saída com grep:

inputcmd | grep -v startdate

informação relacionada