Butschek.de

Warnung: Diese Seite ist eine statische Kopie eines früher betriebenen Blogs. Es findet keine Aktualisierung mehr statt. Kommentare und die Suche funktionieren nicht.

Linux, Server & Co

Archive for the ‘LST’ Category

Scriptverzeichnis finden

2 comments

Verzeichnis-IconIch bastel oft kleine Scripte, die mir irgendwelche Aufgaben abnehmen und meist auch nicht lange leben. Oft arbeite ich bei solchen Helper-Scripts nicht so 100% sauber. Zum Beispiel erstelle ich häufig keine saubere temporäre Datei mit tmpfile, sondern benutze einfach ein File im aktuellen Verzeichnis. Oder ich benötige eine Eingabedatei für das Script und benutze einfach „input.txt“ statt das File per Commandline zu übergeben.

Dabei fiel ich schon einige Male darauf herein, dass solche Scripte oft nicht richtig funktionieren, wenn man sich beim Starten nicht im Verzeichnis des Scripts befindet. Wenn man das Script z.B. unter /usr/local/bin ablegt und es später vom Home-Verzeichnis (~) aus starten (mit /usr/local/bin/script.sh), wird er die Datei „input.txt“ natürlich im Home-Verzeichnis suchen und nicht dort, wo ich sie abgelegt habe.

Ein einfacher Trick, dies global zu lösen: Gleich am Anfang des Scripts mit cd in das Verzeichnis wechseln, in dem das Script liegt. Aber wie findet das Script heraus, wo es liegt?

Die Variable $0 enthält den Scriptnamen, wie er auf der Kommandozeile aufgerufen wurde. Starten wir das Script mit /usr/local/bin/script.sh enthält $0 den String „/usr/local/bin/script.sh“. Sind wir schon im richtigen Verzeichnis, würden wir es mit ./script.sh starten. So enthält $0 auch nur „./script.sh“.

Das Kommando dirname hilft uns weiter. Während basename den Dateinamen aus einem vollständigen Pfad löst, holt dirname nur den Pfad heraus:

# basename /usr/local/bin/script.sh
script.sh
# dirname /usr/local/bin/script.sh
/usr/local/bin
# basename ./script.sh
script.sh
# dirname ./script.sh
.

Nun hilft uns „.“ natürlich nicht sehr weiter, um das tatsächliche Verzeichnis herauszufinden. Aber das benötigen wir ja auch gar nicht, denn es genügt ja, den relativen Pfad zu erhalten, um mit cd dorthin zu wechseln. Und das funktionier auch mit dem aktuellen Verzeichnis „.“ ohne Probleme.

Nun starten wir unser Script wie folgt:

#!/usr/bin/env bash
cd $(dirname $0)

Das „dirname $0“ bringt das Verzeichnis zum Vorschein, so wie es auf der Kommandozeile eingegeben wurde (also absolut oder relativ zum aktuellen Verzeichnis) und mit cd wechseln wir dorthin. Somit wechselt das Script immer in das Verzeichnis, in dem es selbst liegt.

Klar, das ist nur, um die eigene Faulheit zu unterstützen, eine saubere Programmierung wäre natürlich eher zu empfehlen, aber mir half diese Zeile schon recht oft, so dass ich sie meist schon überall einsetze.

Written by Michael Butschek

Dezember 19th, 2012 at 9:16 pm

Posted in IT,LST

Tagged with , , ,

Cron: Letzter Freitag im Monat

leave a comment

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)

Written by Michael Butschek

Dezember 16th, 2012 at 10:00 am

Posted in IT,LST

Tagged with , , , ,

Magic Line

one comment

Shebang

Auf Linux und anderen unixartigen Systemen steht in der ersten Zeile eines Scripts ein Shebang (#!) gefolgt vom Pfad des Interpreters für dieses Script.

Bash-Scripte starten oft mit #!/bin/bash wärend Perl-Scripte mit #!/usr/bin/perl starten. Dem Betriebsystem hilft diese Zeile, direkt den richtigen Interpreter für dieses Script zu finden und so richtig mit der Datei umzugehen.

In vielen Anleitungen wird #!/bin/sh für Shellscripte empfohlen. Ich bin kein Fan davon, denn /bin/sh ist ein Symlink auf die bevorzugte Shell dieses Systems. Debian benutzt hier die dash, manche Admins bevorzugen aber auch die Korn Shell oder die Z-Shell. Hinter /bin/sh kann also je nach System eine andere Shell stehen.

Ich benutze /bin/bash und stelle damit sicher, dass mein unter der Bash geprüfte Script auch auf einer Bash ausgeführt wird. Aber ein BSD User hat mich neulich bekehrt, künftig „/usr/bin/env bash“ zu benutzen.

Der Grund dafür ist folgender: Nicht alle unixartigen Systeme legen die Bash in /bin ab. Manche Systeme haben diese unter /usr/bin. Gibt man nun /bin/bash im Script an, wird das starten des Scripts scheitern.

env ist ein Kommando, dass auf unixartigen Systemen immer in /usr/bin liegt und das Kommando im Parameter (hier: bash) innerhalb des Suchpfades des Systems (PATH-Variable) sucht und startet.

Mit env spielt es also keine Rolle mehr, ob die Shell in /bin, in /usr/bin oder sonstwo im Suchpfad liegt.

Nun, künftig starten meine Scripte also so:
#!/usr/bin/env bash

Written by Michael Butschek

Dezember 9th, 2012 at 11:15 am

Posted in IT,LST

Tagged with , , ,