So teilen Sie den Datumsbereich mithilfe eines Skripts in Tage auf

So teilen Sie den Datumsbereich mithilfe eines Skripts in Tage auf

Ich habe diese Eingabe:

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

in der eine Zeile einen Datumsbereich angibt, der sich über mehrere Tage erstreckt, und ich den Bereich in einzelne Zeiträume aufteilen möchte, von denen jeder eine Teilmenge eines Tages ist (jeder in einer separaten Zeile), um die parallele Verarbeitung des (mehrtägigen) Bereichs zu erleichtern.

Die Ausgabe sollte sein

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

wobei die Daten nach der Endzeit (val1 und val2) in jeder Zeile repliziert werden. 

  1. Tatsächlich stammen die Eingabedatensätze aus der Hive-Tabelle und die Ausgabedatensätze werden ebenfalls in der geteilten Tabelle gespeichert.

Änderungen:

Datumsaufteilung ist in Ordnung. Der val2-Wert muss auch entsprechend dem Aufteilungsdatum aufgeteilt werden.

Wenn der Datumsunterschied 2 ist, dann würden wir 2 Zeilen aufteilen, die sein sollten

  • Reihe 1:

Verhältnis = Verhältnis der aufgewendeten Zeit am 1. Tag (d. h. Ende-Beginn am 1. Tag) /Wert1

Wert2 = Verhältnis * Wert2

  • Reihe 2:

Verhältnis = Verhältnis der aufgewendeten Zeit am 1. Tag (also Ende-Beginn am 2. Tag) /Wert1

Wert2= Verhältnis*Wert2

Wie kann ich das skripten?

Antwort1

Dieses Skript wird tun, was Sie wollen (wenn ich Ihre Anforderungen richtig verstehe). Ich habe mir die Freiheit genommen, Ihre Spezifikation zu extrapolieren, um der Eingabe eine Kopfzeile und dann eine beliebige Anzahl von Zeilen mit Datums-/Zeitbereichen zu ermöglichen. Ich werde dies weiter unten veranschaulichen und weiter erläutern.

#!/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

Diskussion:

  • Lesen Sie die Kopfzeile. Wenn dies fehlschlägt, brechen Sie das Skript ab. Wenn es erfolgreich ist, schreiben Sie die Zeile in die Ausgabe. Wenn Sie (wie Ihre Frage zeigt) die Kopfzeile nicht in Ihrer Ausgabe haben möchten, entfernen Sie die printf "%s\n" "$header"Anweisung.
  • Wie oben erwähnt: Schleife, die Start-/End-/Wertzeilen aus der Eingabe liest, bis wir das Ende der Eingabe erreichen (oder einen schwerwiegenden Fehler erhalten). Wenn Sie dies nicht tun möchten, entfernen Sie die while, die dound die entsprechenden done.
  • Liest Startdatum, Startzeit, Enddatum, Endzeit und andere Daten.  other_dataSchließt alles nach der Endzeit ein, also val1 und val2 (und alle Leerzeichen dazwischen).
  • Verwenden Sie den Befehl, um beliebige Datums-/Zeitzeichenfolgen in Unix-Epochenzeiten umzuwandeln – die Anzahl der Sekunden seit 1970-01-01 00:00:00 (GMT). Dadurch können wir die Eingabe validieren (und im Fehlerfall beenden) und erhalten außerdem Zahlen, die wir vergleichen können. (Obwohl wir vermutlich einfach Zeichenfolgenvergleiche mit Werten im Format JJJJ-MM-TT HH:MM:SS durchführen könnten.)date +"%s" -d "date/time string"
  • Wenn das Enddatum/die Endzeit vor dem Startdatum/der Startzeit liegt, überspringen Sie diesen Datensatz und fahren Sie mit der nächsten Zeile fort. Wenn Sie in diesem Fall lieber etwas anderes tun möchten (z. B. beenden), ändern Sie diesen Code.
  • Setzen Sie ein Flag ( ok_seq), das wir zur Steuerung der Schleife verwenden, die durch die Tage geht. Initialisieren Sie das Startdatum/die Startzeit für den ersten Tag als Startdatum/die Startzeit für den gesamten Zeitraum.
  • In jeder Ausgabezeile sind das Startdatum und das Enddatum gleich. In den meisten Zeilen ist die Endzeit des Tages (EOD) 23:59:59. Wenn (dasselbe Datum) + 23:59:59 größer (später) ist als das Datum/die Uhrzeit des Periodenendes, dann befinden wir uns am letzten Tag (Ausgabezeile) des Bereichs. Setzen Sie die EOD-Zeit auf die Endzeit und auf ok_seq0, damit wir die Schleife verlassen.
  • Schreiben Sie eine Ausgabezeile, einschließlich der „anderen Daten“ (val1 und val2 usw.).
  • Berechnen Sie das Datum des nächsten Tages. Setzen Sie die Startzeit auf 00:00:01, die in jeder Ausgabezeile außer der ersten angezeigt wird.

Beispiel:

$ 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

Beachten Sie, dass eine Übertragung nicht nur von einem Monat auf den nächsten, sondern auch von einem Jahr auf das nächste problemlos möglich ist.


Notiz: Als ich die obige Version des Skripts schrieb, konnte ich nicht herausfinden, wie ich das Leerzeichen zwischen der Endzeit und val1 erfassen sollte, daher erhielt ich eine Ausgabe, die wie folgt aussah

      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

also habe ich „geschummelt“, indem ich die „richtige Menge“ Leerzeichen in den printfBefehl eingebaut habe (vor dem letzten %s). Wenn Sie jedoch den Abstand in Ihrer Eingabe ändern, erzeugt die obige Version des Skripts wieder falsch ausgerichtete Spalten. Ich habe herausgefunden, wie man das behebt, obwohl es ein bisschen chaotisch ist. Ersetzen Sie die while … dostart_epoch=…Zeilen durch:

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_timewurde aus dem readBefehl entfernt und die Zeichen zwischen den Klammern [und dem ] sind ein Leerzeichen und ein Tabulator. other_dataEnthält also jetzt die Leerzeichen vor val1. Ändern Sie dann das printfin

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

(Beachten Sie, dass esNEINLeerzeichen zwischen dem vierten und fünften %s). Jetzt sind Sie fertig.

Antwort2

Ich vermute, Sie möchten die oberste Kopfzeile loswerden. Nehmen wir an, die Funktion, von der Sie diese Eingabe erhalten, heißt „timefunc“. Sie könnten versuchen, die Ausgabe von timefunc in einem Cut-Befehl wie diesem weiterzuleiten:

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

Die Ausgabe ist jetzt:

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

Antwort3

Sie können die Kopfzeilen mit grep aus Ihrer Ausgabe entfernen:

inputcmd | grep -v startdate

verwandte Informationen