
У меня есть следующие данные:
startdate end date val1 val2
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
в котором одна строка указывает диапазон дат, охватывающий несколько дней, и я хочу разбить диапазон на отдельные периоды времени, каждый из которых будет подмножеством дня (каждый на отдельной строке), чтобы облегчить параллельную обработку (многодневного) диапазона.
Выход должен быть
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
где данные после конечного времени (val1 и val2) реплицируются в каждой строке.
- На самом деле входные записи поступают из таблицы Hive, а выходные записи также будут сохраняться в разделенной таблице.
Модификации:
Разделение по дате нормально. Необходимо также разделить значение val2 в соответствии с датой разделения.
если разница дат равна 2, то мы разделим 2 строки, которые должны быть
- ряд 1:
отношение = отношение времени, затраченного на 1-й день (т.е. конец-начало в 1-й день) /val1
val2= отношение*val2
- ряд 2:
отношение = отношение времени, затраченного на 1-й день (т.е. конец-начало на 2-й день) /val1
val2= отношение*знач2
Как мне это написать?
решение1
Этот скрипт сделает то, что вы хотите (если я правильно понял ваши требования). Я взял на себя смелость экстраполировать вашу спецификацию, чтобы разрешить вводу иметь одну строку заголовка, а затем любое количество строк с диапазонами даты/времени. Я проиллюстрирую это и обсужу подробнее ниже.
#!/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
Обсуждение:
- Прочитайте строку заголовка. Если это не удалось, прервите выполнение скрипта. Если это удалось, запишите строку в вывод. Если (как показывает ваш вопрос) вы не хотите, чтобы заголовок был в вашем выводе, удалите оператор
printf "%s\n" "$header"
. - Как упоминалось выше: цикл, чтение строк начала/конца/значения из ввода, пока не достигнем конца ввода (или не получим фатальную ошибку). Если вы не хотите этого делать, удалите
while
,do
и соответствующийdone
. - Считывает начальную дату, время начала, конечную дату, время окончания и другие данные.
other_data
Включает все после времени окончания, т. е. val1 и val2 (и все пространство между ними). - Используйте команду для преобразования произвольных строк даты/времени в Unix «время эпохи» — количество секунд с 1970-01-01 00:00:00 (GMT). Это позволяет нам проверять ввод (и выходить в случае ошибки), а также дает нам числа, которые мы можем сравнивать. (Хотя я предполагаю, что мы могли бы просто сравнивать строки со значениями, отформатированными как YYYY-MM-DD HH:MM:SS.)
date +"%s" -d "date/time string"
- Если конечная дата/время предшествует начальной дате/времени, пропустите эту запись и перейдите к следующей строке. Если вы хотите сделать что-то другое (например, завершить) в этом случае, измените этот код.
- Установите флаг (
ok_seq
), который мы будем использовать для управления циклом, проходящим через дни. Инициализируйте начальную дату/время для первого дня, чтобы они стали начальной датой/временем для всего периода. - В каждой выходной строке начальная и конечная даты совпадают. В большинстве строк время конца дня (eod) — 23:59:59. Если (та же дата) + 23:59:59 больше (позже) даты/времени конца периода, то мы находимся на последнем дне (выходной строке) диапазона. Установите время eod на конечное время и установите
ok_seq
на 0, чтобы выйти из цикла. - Запишите строку вывода, включая «другие данные» (val1 и val2 и т. д.)
- Вычислите дату следующего дня. Установите начальное время на 00:00:01, которое появится в каждой выходной строке, кроме первой.
Пример:
$ 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
Обратите внимание, что проблем с переходом не возникает не только с одного месяца на другой, но и с одного года на другой.
Примечание: Когда я писал приведенную выше версию скрипта, я не мог понять, как захватить пробел между конечным временем и val1, поэтому я получал вывод, который выглядел следующим образом:
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
︙
поэтому я «схитрил», встроив «правильное количество» пространства в printf
команду (перед последним %s
). Но если вы измените интервал во входных данных, приведенная выше версия скрипта снова выведет неправильно выровненные столбцы. Я понял, как это исправить, хотя это немного беспорядочно. Замените строки while …
do
… start_epoch=…
на:
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=…
where end_time
был удален из read
команды, а символы между скобками [
и ]
являются пробелом и табуляцией. Так что теперь other_data
содержит пробелы перед val1. Затем измените printf
на
printf "%s %s %s %s%s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"
(обратите внимание, что естьнетпробел между четвертым и пятым %s
). Итак, теперь все готово.
решение2
Я предполагаю, что вы хотите избавиться от верхней строки заголовка. Допустим, функция, из которой вы получаете эти входные данные, называется 'timefunc'. Вы можете попробовать перенаправить вывод timefunc в команду cut, например так:
timefunc | cut -d$'\n' -f2
Теперь вывод такой:
2015-10-13 07:00:02 2015-10-19 00:00:00 45 1900
решение3
Вы можете удалить строки заголовка из вывода с помощью grep:
inputcmd | grep -v startdate