Rettet das Datum!

18. März 2013 at 22:44

Das kleine Kommando ‚date‘ der Linux-Shell wird oft mächtig unterschätzt. Man kann das Ausgabeformat setzen und sogar das Datum relativ zum heutigen Tag bestimmen. Hierfür gibt es viele Anwendungsfälle:

# Datum ausgeben
date
Mon Mar 18 20:00:00 CET 2013
# Das Datum von gestern
date -d "yesterday"
Sun Mar 17 20:00:00 CET 2013
# Das Datum von vor 2 Tagen
date -d "2 days ago"
Sat Mar 16 20:00:00 CET 2013
# Das Datum im ISO-Format
date +"%Y-%m-%d"
2013-03-18

Hier nun eine Kombination daraus:

# Das Datum von gestern im ISO-Format
date +"%Y-%m-%d" -d "yesterday"
2013-03-17

Nun erzeugen wir einen Dateinamen:

date +"something-%Y-%m-%d.log" -d "yesterday"
something-2013-03-17.log

Wenn wir den Namen in einer Variable benötigen:

FILENAME=$(date +"something-%Y-%m-%d.log" -d "yesterday")
echo "$FILENAME"

Ein anderer Anwenungsfall: Bestimmen wir die Zeit zwischen 2 Zeitpunkten:

START=$(date "+%s")
(do something)
ENDE=$(date "+%s")
let "DIFF = $ENDE - $START"
echo "$DIFF Sekunden"

Nun erhalten wir die Zeit-Differenz in Sekunden. Aber was sagt uns schon 2800 Sekunden? Auch hier kann date helfen, die Zeit umzurechnen:

date -d "00:00:00 $DIFF seconds" "+%H:%M:%S"
00:46:40

Das obige Script lief also 46 Minuten und 40 Sekunden. So läßt sich dem nächtlichen Backup-Script schnell beibringen, wie es seine Laufzeit auszugeben hat.

Noch ein Trick: Mit folgendem Befehl kann man die 3 Variablen $DAY, $MONTH und $YEAR in einer Zeile setzen:

eval $(date "+DAY=%d; MONT=%m; YEAR=%Y")

Nun läßt sich mit ‚echo $YEAR‘, ‚echo $MONT‘ oder ‚echo $DAY‘ das Datum im Script abfragen.

Ach ja, wie alt bin ich eigentlich?

BIRTHDAY="1975-01-10"

BDAY_SEC=$(date "+%s" -d "$BIRTHDAY")
NOW_SEC=$(date "+%s")

let "DIFF_SEC = $NOW_SEC - $BDAY_SEC"
echo "Alter: $DIFF_SEC Sekunden"

let "DIFF_DAYS = $DIFF_SEC / 60 / 60 / 24"
echo "Alter: $DIFF_DAYS Tage"

Es gibt so viele Anwendungsfälle. Also, rettet das Datum! :-)

Mehr zum Thema date: hier

Cron: Letzter Freitag im Monat

16. Dezember 2012 at 10:00

Viele regelmäßig Aufgaben erledigen Admins mit Script, die per Cron gestartet werden. Solange sich das Zeitmuster im Format von Cron eingeben läßt, ist das alles kein Problem.

  • 0 9 * * * = Jeden Tag um 9 Uhr
  • 30 14 * * 1-5 = Wochentags um 14:30 Uhr

Aber was, wenn ein Script z.B. am letzten Freitag im Monat gestartet werden soll? Das läßt sich in diesem Muster nicht darstellen. Es gibt aber ein paar einfache Tricks, die in Kombination zum Ziel dieser Aufgabe führen.

Trick 1: Kommandos mit && Verknüpfen

Mehrere Kommandos lassen sich mit && verknüpfen. Die Shell führt dabei das zweite Kommando nur aus, wenn das erste Kommando erfolgreich war (Exitcode 0):

/usr/local/daten_einsammeln.sh && /usr/local/daten_verarbeiten.sh

Somit kann man die Ausführung des zweiten Scripts (Verarbeiten der Daten) vom Erfolg des ersten Scripts (Sammeln der Daten) abhängig machen.

Trick 2: Abfragen ohne if mit [[ und ]]

Man kann mit [[ und ]] Bedingungen erstellen, ohne ‚if‘ einzusetzen. Ist die Bedingung erfüllt, wird Exitcode 0 zurückgemeldet. In Kombination mit Trick 1 läßt sich so eine bedingte Ausführung realisieren, ohne mit if-then-fi arbeiten zu müssen:

[[ bedingung ]] && kommando

Trick 3: Letzten Tag finden

Sucht man den letzten Freitag im Monat, so kann man das ganz einfach in eine Bedingung packen:

  • date -d ‚1 week‘ ‚+%m‘ ergibt den Monat des heutigen Datums.
  • date -d ‚1 week‘ ‚+%m‘ ergibt den Monat des Datums in genau einer Woche.

Jetzt einfache mathematische Logik: Wenn der Monat in genau einer Woche anders ist als heute, dann ist der heutige Wochentag der letzte in diesem Monat vorkommende.

Anders gesagt: Wenn wir in 7 Tagen in einem anderen Monat sind UND heute Freitag ist, dann ist heute der letzte Freitag im Monat.

Kombination der Tricks

Wir starten einen Cronjob jeden Freitag und nutzen den Trick 2, um das Kommando nur auszuführen, wenn eine Bedingung erfüllt ist. Die Bedingung ist aus Trick 3, wir prüfen nämlich, ob nächsten Freitag schon ein neuer Monat ist. Das sieht so aus:

[[ $(/bin/date -d '1 week' '+%m') -ne $(/bin/date '+%m') ]] && kommando

Somit haben wir einen Cronjob, der jeden Freitag gestartet wird, aber das gewünschte Kommando jeweils nur am letzten Freitag des Monats ausführt.

Hier eine Beispieldatei (/etc/cron.d/abrechnung), die Cron so akzeptieren wird:

MAILTO="cronjobs@butschek.de"
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

#  +--------------------------------------------- Minute      (0-59)
#  |      +-------------------------------------- Stunde      (0-24)
#  |      |      +------------------------------- Kalendertag (1-31)
#  |      |      |      +------------------------ Monat       (1-12)
#  |      |      |      |      +----------------- Wochentag   (1-7 = Mo-So)
#  |      |      |      |      |      +---------- Benutzer    (z.B. root)
#  |      |      |      |      |      |      +--- Kommando    (Pfad + Filename)
#  |      |      |      |      |      |      |

# Abrechnung am Letzten Freitag im Monat starten
  30     10      *      *      5      root   [[ $(/bin/date -d '1 week' '+%m') -ne $(/bin/date '+%m') ]] && /usr/local/abrechnung

#  |      |      |      |      |      |      |
#  |      |      |      |      |      |      +--- Kommando    (Pfad + Filename)
#  |      |      |      |      |      +---------- Benutzer    (z.B. root)
#  |      |      |      |      +----------------- Wochentag   (1-7 = Mo-So
#  |      |      |      +------------------------ Monat       (1-12)
#  |      |      +------------------------------- Kalendertag (1-31)
#  |      +-------------------------------------- Stunde      (0-24)
#  +--------------------------------------------- Minute      (0-59)

ISO-Datum in Windows Batch-Files

3. Juni 2010 at 22:52

Wer unter Windows mit Batch-Files Scriptet, dürfte die Variablen %time% und %DATE% kennen, die die Uhrzeit sowie das aktuelle Datum im lokalen Format „dd.mm.yyyy“ zurückgeben.

Manchmal möchte man Dateien oder Verzeichnisse nach dem aktuellen Datum benennen, z.B. Logfiles, Zip-Dateien oder ein Backup-Verzeichnis für das tägliche Backup.

set dirname="C:Backup-%DATE%"
mkdir %dirname%

Einen großen Nachteil hat dieser Weg: Beim alphabetischen Anordnen im Windows-Explorer ist der 20. Mai vor dem 31. Januar. Der einfachte Weg ist die Verwendung des ISO-Formats JJJJ-MM-TT, also zuerst Jahr, dann Monat, dann Tag. So wird immer erst nach dem Größten (Jahr), zum Ende nach dem Kleinsten (Tag) sortiert.

Leider bietet die Windows Commandline ein solches Format nicht an, das läßt sich aber leicht korrigieren: Wir nehmen einfach aus %DATE% die nötigen Stellen heraus und basteln ein eigenes Format:

set TAG=%date:~-10,2%
set MONAT=%date:~-7,2%
set JAHR=%date:~-4%
set ISODATE=%JAHR%-%MONAT%-%TAG%

Fertig ist unser neues Datum %ISODATE%, das wir nun statt %DATE% verwenden:

set dirname="C:Backup-%ISODATE%"
mkdir %dirname%