Como dividir o intervalo de datas em dias usando o script

0

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 período que abrange vários dias, e eu 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 da faixa (de vários dias).

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 final (val1 e val2) são replicados em cada linha.

  1. Na verdade, os registros de entrada são provenientes da tabela de ramificação e os registros de saída também serão armazenados na tabela dividida.

Modificações:

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

se a data diff for 2, dividiríamos duas linhas que deveriam ser

  • linha 1:
Relação

= proporção de vezes 1 dia (isto é, início no dia 1) / val1

val2 = relação * val2

  • linha 2:
Relação

= razão do tempo gasto no primeiro dia (ou seja, início no final do dia 2) / val1

val2 = relação * val2

Como posso escrever isso?

    
por AAA 21.10.2016 / 23:27

3 respostas

0

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 , o do e o correspondente done .
  • 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 … dostart_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.

    
por 23.10.2016 / 06:33
0

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

inputcmd | grep -v startdate
    
por 22.10.2016 / 00:28
0

Suponho que você esteja tentando se livrar da linha de cabeçalho principal. Digamos que a função da qual você está recebendo esta entrada é chamada de 'timefunc'. Você pode tentar testar a saída do timefunc em um comando de corte 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
    
por 22.10.2016 / 00:09