如何使用腳本將日期範圍分割為天

如何使用腳本將日期範圍分割為天

我有這個意見:

      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)在每行上複製。 

  1. 實際上輸入記錄來自 hive 表,輸出記錄也將其儲存在 split 表中。

修改:

日期分割很好。還需要根據分割日期分割 val2 值。

如果日期差異為 2 那麼我們將分割 2 行,這應該是

  • 第 1 行:

比率=第一天花費的時間比率(即第一天的結束-開始)/值1

val2=比率*val2

  • 第 2 行:

比率=第一天花費的時間比率(即第二天的結束-開始)/值1

值2= 比率*val2

我該如何編寫這個腳本?

答案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"語句。
  • 如上所述:循環,從輸入中讀取開始/結束/值行,直到到達輸入的末端(或出現致命錯誤)。如果您不想這樣做,請刪除whiledo和相應的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=…

whereend_time已從read命令中刪除,括號[和 the之間的字元] 是空格和製表符。所以現在other_data包含 val1 之前的空格。然後將其更改printf

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

(請注意,有第四個和第五個之間的空間%s)。現在你已經完成了。

答案2

我猜您正在尋求擺脫頂部標題行。假設您從中取得輸入的函數稱為“timefunc”。您可能會想嘗試在 cut 指令中管道 timefunc 的輸出,如下所示:

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

相關內容