
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.
- 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
, odo
e o correspondentedone
. - Leia a data de início, hora de início, data de término, 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 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_seq
como 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 printf
comando (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 …
do
… start_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_time
foi removido do read
comando e os caracteres entre colchetes [
e ]
são um espaço e uma tabulação. Então agora other_data
contém os espaços antes de val1. Então mude printf
para
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