Archiv der Kategorie: Linux

command not foundDieses Bild hat ein leeres alt-Attribut; sein Dateiname ist shira128x128.png.

Shira, meine Katze, kommt sporadisch zur Tastatur, und reibt ihren Kopf daran – v.a. wenn ich die Tastatur auf dem Schlafsofa auf dem Rücken liegend auf dem Bauch balanciere.

Sie schrubbt sich den Nacken und gibt v.a. die am Rande liegenden Zeichen des Ziffernblocks 0,+ und Enter ein. Das sind keine installierten Kommandos/Programmnamen, so dass da nicht allzu viel passieren kann, aber der Rechner reagiert allenfalls mit

000,000+0000++++,,,: Befehl nicht gefunden.

Das ist natürlich nicht sehr einladend für meine Katze. Meine erste Lösung (v0.1) war, dass der Rechner bei signifikanten Shirakommandos mit `echo Hallo Shira` reagiert. Aber Lesen ist nicht ihre Stärke.

Also veröffentlichte ich bald darauf v0.2, die eine Audioaufnahme von 2-3 Sekunden abspielt, die „Hallo Shira, miau“ wiedergibt.

Auf Github https://github.com/Stefan-Wagner/shira kann man den Code runterladen.

Im Wesentlichen sind es 2-3 zusätzliche Zeilen, die ich der Funktion, die ich in /etc/bash.bashrc gefunden habe, zugefügt habe, und ich lade dieses in meinem eigenen /bin-Ordner gespeicherte Script mit dieser Funktion aus meiner ~/.bashrc via source-Befehl (`source ~/bin/shira.sh`) um System und meinen Kram sauber getrennt zu halten.

Ändert man die /etc/bash.bashrc direkt, bekommt man evtl. Konflikte bei Updates. Migriert man all seine Einstellungen auf einen anderen Rechner, vergisst man solche Details auch gerne.

Im Unterschied zur Originalfunktion beginnt meine mit:

if [[ $1 =~ ^[0,+]+$ ]]
then
    echo hallo $katze
    aplay ~/lib/helloMyCat.wav 2>/dev/null &
elif [ -x /usr/lib/command-not-found ]; then

Das GLEICH-TILDE ist ein regex-Vergleich, und `[0,+]` beschreibt die Gruppe der 3 Zeichen 0, Komma und Plus, von denen mindestens eins, aber beliebig viele vorkommen können (+), und es funktioniert wie gewollt, es gibt „Hallo Shira“ auf dem Bildschirm und „Hallo Shira, Miau“ auf dem Lautsprecher aus. In Zeile 5, beim elif, beginnt das Original erst mit if.

Wenn Sie auch etwas auf dem Lautsprecher ausgeben wollen, müssen Sie es zuvor selbst aufnehmen und entsprechend speichern und aufrufen. Ich habe mir einen eigenen Ordner ~/lib gemacht, in dem ich die Datei gespeichert habe, aber für viele Nutzer wird das übertrieben sein.

fcd (fast-cd) Version 1.1

Bugfix/Update nach Hinweisen von m-bostaurus.

Beim Aufruf von fcd mit bin als Zielordner erlebte m-bostaurus, dass nur 1 Verzeichnis gefunden wurde. Meine erste Vermutung, es könne daran liegen, dass zu viele Treffer gefunden werden, und die Parameterliste zu lang ist, bewahrheitete sich zwar nicht, aber mit dem Umbau, der das Problem beheben sollte, war es behoben.

Ich verwende jetzt `mapfile`, um die Suchergebnisse in ein Array zu packen.

Hier der neue Code:

#!/bin/bash
#
# fcd.sh:
#
# Fast change directory. Should be sourced from ~/.bashrc file, since it is and
# needs to be a function.
#
#	Update Ver.1.1, 2021-03-03
#		Bugfix for missing results when fast changing to "bin", thanks to m_bostaurus.
#	Update Ver.1.0, 2021-02-07
#		changed from egrep to mapfile, see code. Handles blanks in paths now flawlessly.
#   Update Ver.0.9, 2013-08-21, comments, publication
#
#   Prototyp 2010
#
# This program depends on locate/updatedb, which aren't always installed/activated.
# If you use the shell on a regular basis, you should have them installed, though.
#
#	(c) GPLv3 (2010, 2021)
#
fcd ()
{
	suchdirname=$1
	# list=$(locate $1 | egrep "/$1$"); # Version 1
    # update 12.02.2021
    # mapfile -d '' list < <(locate -b -0 -r "$suchdirname$")
    # update 19.02.2021
    mapfile -d '' list < <(locate -b -0 -r "^$suchdirname$")
    # count=$(echo $list | wc -w );
    # update 19.02.2021
    count=${#list[@]}
    case $count in
        0)
            echo "unknown directory: "$1 && return
            # could search for partial matches Doc => Documentation
        ;;
        1)
            if [[ -d "${list[0]}" ]]; then
                echo "1) match: $list";
                cd "$list";
            else
                echo "not a directory: $1";
            fi
        ;;
        *)
            select directory in "${list[@]}" "/exit/";
            do
                if [[ "$directory" = "/exit/" ]]; then
                    break;
                fi;
                if [[ -d "$directory" ]]; then
                    echo "multi) $directory";
                    cd "$directory";
                    break;
                else
                    echo "not a directory: "$1;
                fi;
            done
        ;;
    esac
}

Der neue Code ist jetzt 2 Wochen bei mir im Einsatz, und funktioniert tadellos.

Wenn aber doch jmd. noch Fehler findet – immer her damit!

Es geht bergauf!

Es geht bergauf und es ist nicht schön. Jetzt hatten wir wochenlang Teillockdown und die Diskussionen gingen gerade los, wer zuerst aufmachen darf, mit Frisören und Schulen in Reihe eins, und dann sowas!

Für Mo., Di., Mi. und Do. zeigt der Wochentagsgraph (basierend auf den Daten von worldometers) hinten einen kleinen Anstieg – allerdings nicht für Fr. (gestern).

Woher sind die Grapen? Nun, mit einem Script selbst generiert. Der schleichende Durchschnitt hat auch seine Berechtigung – zumindest der über 7 Tage, in dem über 3 Tage sehe ich keinen Sinn, und die Haifischzähne im Wochenverlauf scheinen ein globales Phänomen zu sein.

Ich fürchte, dass während die Zahlen maßnahmenbedingt zurückgingen stiegen gleichzeitig die Zahlen durch die Mutanten im Untergrund an, und jetzt haben sich die beiden Trends gekreuzt. Ich bin da aber natürlich Laie, wie viele andere auch.

Das Basteln der ersten Graphen war noch viel Handarbeit, aber dann wurde es von mir teilautomatisiert, so dass das Script die Daten jeden Tag neu runterlädt.

#!/bin/bash
# 
# Coronadaten von worldometers nach Wochentag getrennt anzeigen
#
# @DONE: Lines einfärben nach Wochentag
# @DONE: Automatisieren. 
#
# Lizenz: GPLv3  
#

yvals=($(wget -O - https://www.worldometers.info/coronavirus/country/germany/ 2>/dev/null | sed -ne "s/,/ /g; /name: 'Daily Cases'/,/]/ {s/data: \[null//; s/].*}//p}"))

ny=${#yvals[@]}

for i in $(seq 0 $((ny-1)))
do
	echo -e "$i\t${yvals[$i]}"
done > xy.dat

tag=(dummy Sa So Mo Di Mi Do Fr)
farbe=(dummy black blue olive red dark-grey grey web-green)

max () {
	echo $@ | tr " " "\n" | sort -n | tail -n 1
}

maxy=$(max ${yvals[@]})

zeros=${maxy//[0-9]/0}
top=$((${maxy:0:2}+1))${maxy:3:-0}${zeros:2}
echo top $top

# leeren
echo >  plot7.plt

# In 7-Tagesschritten von Sa. bis Fr. (weil Sa. 1. Meßpunkt ist, Sa:=1) durchlaufen, getrennte Dateien erzeugen
for i in {1..7}; do sed -n "$i~7p" xy.dat > wotag-$i-${tag[$i]}.csv ; done
# Gnuplot-Datei erzeugen
for i in {1..7}; do
	echo "
	set term qt
	set xrange [0:$((ny*105/100))]
	set yrange [0:$top]
	set title \"${tag[$i]}\"
	plot \"wotag-$i-${tag[$i]}.csv\" using 1:2 with linespoint lc \"${farbe[$i]}\"
	set output  \"wotag-$i-${tag[$i]}.png\"
	set term png
	replot"
done >> plot7.plt
gnuplot -p plot7.plt
# eog: Eye-of-Gnome Bildbetrachter.
eog wotag-[1-7]-*.png

(Update 22.2.’21 Das Ausgabeformat habe ich nachträglich auf png geändert (set term png; set output ….png) und den Bildbetrachteraufruf eog am Ende angefügt.)

Anfangs speicherte ich die Seite vom Browser aus im Modus „Seitenquelltext anzeigen“. Er liefert keine fertige Bilddatei aus, sondern die Daten und generiert aus diesen die Grafik on the fly. So kommt man also einfach an die Daten.

Zu Beginn zeigt das Script einen Shebang, der dem Kernel sagt, wie das Programm auszuführen ist. Es folgen Kommentare mit der Gnu-Public-Lizence.

Dann der Code, um das HTML als Datei runterzuladen, dafür nimmt man wget.

yvals=($(wget -O - https://www.worldometers.info/coronavirus/country/germany/ 2>/dev/null | sed -ne "s/,/ /g; /name: 'Daily Cases'/,/]/ {s/data: [null//; s/].*}//p}"))

Mit -O gibt man den Outputort an, meist einen Dateinamen, aber wie viele Shellwerkzeuge, kann auch wget ein isoliertes Minuszeichen übergeben werden, dann wird die Ausgabe auf Stdout, den Bildschirm, ausgegeben.

Aber wget ist geschwätzig und zeigt mit großem Brimborium an, wieviel Prozent schon runtergeladen wurden, Datum, Uhrzeit, Url, IP-Adresse und weiß der Teufel – den Müll kann man mit 2>/dev/null ins Nirwana umleiten.

Der restliche Output wird mit einer Pipe an Sed geleitet, den Stream-Editor, spezialisiert für solche Jobs und mit einer konzentrierten Syntax ausgestattet, um die Ausgabe zu schneiden, umzuformen, zu ergänzen, zu manipulieren.

Mit sed wandel ich alle Kommas in Leerzeichen um. Ich will alles in ein großes Array schreiben, und die Syntax sieht so aus:

yvals=(3 4 2 1 9)

Aber die Quelle liefert

data: [3,4,2,1,9]

Auch interessiert mich nur der Abschnitt „Daily Cases“ und darin nur die Zeile „data“. Die nächste, kryptisch wirkende Zeile, ermittelt die Arraygröße.

Die folgende Forschleife läuft durch alle Werte, und gibt Zeilenweise die Indexnummer und die Anzahl bestätigter Neuinfektionen aus. Die Umlenkung hinter der Schleife lenkt all das in eine Datei xy.dat.

ny=${#yvals[@]}

for i in $(seq 0 $((ny-1)))
do
	echo -e "$i\t${yvals[$i]}"
done > xy.dat

Die Beispieldaten oben sähen damit etwa so aus:

0	3
1	4
2	2
3	1
4	9

Dann folgen zwei Arrays mit Namen für die Wochentage – da die Shell ab 0 zählt, aber ich ab 1, habe ich einen dummy für die 0 eingefügt. Ähnlich für die Farben des Graphen.

tag=(dummy Sa So Mo Di Mi Do Fr)
farbe=(dummy black blue olive red dark-grey grey web-green)

max () {
	echo $@ | tr " " "\n" | sort -n | tail -n 1
}

Dann sehen wir eine max-Funktion. Sie übergibt alle Argumente des Arrays, die mit Leerzeichen getrennt sind, und ersetzt die Leerzeichen mit tr, dem Zeichentranslator, mit Zeilenumbrüchen. Die Zeilen werden mit sort sortiert und tail -n1 nimmt vom Ende eine Zeile, da steht nach Adam Riese das Maximum drin.

Die nächste Zeile ruft die max-Funktion mit unserem Array auf.

maxy=$(max ${yvals[@]})

zeros=${maxy//[0-9]/0}
top=$((${maxy:0:2}+1))${maxy:3:-0}${zeros:2}
echo top $top

Dann folgt eine komplexe Häßlichkeit, die einen Wert wie 31554 nimmt, die 31 vorne abschneidet, zu 32 hochrechnet, und 000 hinten anhängt aber auch mit Werten zwischen 100 und 99999999 zurechtkommen sollte.

Sie dient dazu dem Plotprogramm zu sagen, wie groß der y-Wert werden darf, aber einen aufgerundeten Wert zu verwenden mit etwas Luft nach oben. Da die Werte je nach Wochentag gehörig schwanken führt die automatische Range-auswertung von gnuplot sonst dazu, dass der Achsenmaßstab von Tag zu Tag schwankt.

# leeren
echo >  plot7.plt

Dies löscht die plot7.plt-Datei von gestern oder neulich. Dann schreibe ich, wieder mit sed, das ein Kommando dafür hat, jede 7. Zeile in eine Datei, und das für 7 unterschiedliche Startwerte, sprich Wochentage, in 7 unterschiedliche Zieldateien (.csv).

for i in {1..7}
do
  sed -n "$i~7p" xy.dat > wotag-$i-${tag[$i]}.csv
done

Dann werden je Wochentag ca. 8 Befehle in die Plotdatei geschrieben.

# Gnuplot-Datei erzeugen
for i in {1..7}; do
	echo "
	set term qt
	set xrange [0:$((ny*105/100))]
	set yrange [0:$top]
	set title \"${tag[$i]}\"
	plot \"wotag-$i-${tag[$i]}.csv\" using 1:2 with linespoint lc \"${farbe[$i]}\"
	set output  \"wotag-$i-${tag[$i]}.png\"
	set term png
	replot"
done >> plot7.plt

Das set term qt setzt die Ausgabe auf den Grafikfenstermodus. QT ist eine Firma, QT-Trolltech o.ä., die eine bekannte, plattformübergreifende Grafikbibliothek für C++ geschrieben hat, ähnlich wie GTK (C) oder Swing (Java).

Mit set xrange [von:bis]` legt man die x-Achse fest. Auch hier lasse ich etwas Luft.

Das `set title XY` bringt die Wochentage von oben ins Bild. Man will nicht im Kopf behalten müssen, dass die Datenerfassung an einem Freitag begann – oder war es Samstag?

Dann wird verklausuliert gesagt, dass die Daten von – Beispielsweise wotag-3-Mo.csv zu plotten sind. Dann ist So=2 und Sa=1, also doch Sa! Using 1:2 sind die Spalten der Datei, mehr gibt’s eh nicht, und linespoint verbindet die Datenpunkte, hebt sie aber auch vor. `lc N` setzt die linecolor, falls das wer manipulieren will. Für jeden Tag gebe ich dann einen Dateinamen an, in den das Ergebnis geschieben werden soll. Man könnte auch png, jpg, pdf usw. wählen.

Mit set term png sage ich dem Programm, dass das Format png sein soll; das extrapoliert gnuplot nicht aus dem Namen.

Oben wurde schon mal plot aufgerufen. Hier reicht ein `replot`, wenn man keinen Parameter ändern will. Das eine zeigt am Schirm in einem Fenster an, das andere schreibt in eine Datei.

Die Befehle werden aber nicht unmittelbar ausgeführt, sondern in eine Datei geschrieben und am Ende wird gnuplot aufgerufen, um die Aktionen durchzuführen.

Dabei wird die Bildschirmausgabe aber so schnell durchlaufen, dass man außer dem letzten Bild nichts sieht. Zur Kontrolle, ob nichts schiefläuft, sollte das reichen.

Alle Dateien landen im aktuellen Verzeichnis, also empfiehlt es sich dafür ein neues Verzeichnis anzulegen.

Zusammengefügt habe ich ursprünglich 7 svg-Dateien von Hand.

Das habe nun zu PNG geändert, denn die lassen sich mit einem Betrachter wie eog leicht alle ansehen, oder als Referenzen in ein SVG laden, das sich dann automatisch aktualisiert, wenn die Dateien mit altem Namen und neuen Daten überschrieben werden.

Die CSV-Dateien sind z.Zt. kl. als 500 Bytes, die PNGs < 6kb, je.

Das Datum aus den xValues zu extrahieren ist möglich, aber eine lesbare Ausgabe daraus zu bauen ist mir zu viel Gefummel.

So sieht die fertige Plotdatei aus (hier noch mit SVGs):

	set term qt
	set xrange [0:388]
	set yrange [0:32000]
	set title "Sa"
	plot "wotag-1-Sa.csv" using 1:2 with linespoint lc "black"
	set output  "wotag-1-Sa.svg"
	set term svg
	replot

	set term qt
	set xrange [0:388]
	set yrange [0:32000]
	set title "So"
	plot "wotag-2-So.csv" using 1:2 with linespoint lc "blue"
	set output  "wotag-2-So.svg"
	set term svg
	replot

	set term qt
	set xrange [0:388]
	set yrange [0:32000]
	set title "Mo"
	plot "wotag-3-Mo.csv" using 1:2 with linespoint lc "olive"
	set output  "wotag-3-Mo.svg"
	set term svg
	replot

	set term qt
	set xrange [0:388]
	set yrange [0:32000]
	set title "Di"
	plot "wotag-4-Di.csv" using 1:2 with linespoint lc "red"
	set output  "wotag-4-Di.svg"
	set term svg
	replot

	set term qt
	set xrange [0:388]
	set yrange [0:32000]
	set title "Mi"
	plot "wotag-5-Mi.csv" using 1:2 with linespoint lc "dark-grey"
	set output  "wotag-5-Mi.svg"
	set term svg
	replot

	set term qt
	set xrange [0:388]
	set yrange [0:32000]
	set title "Do"
	plot "wotag-6-Do.csv" using 1:2 with linespoint lc "grey"
	set output  "wotag-6-Do.svg"
	set term svg
	replot

	set term qt
	set xrange [0:388]
	set yrange [0:32000]
	set title "Fr"
	plot "wotag-7-Fr.csv" using 1:2 with linespoint lc "web-green"
	set output  "wotag-7-Fr.svg"
	set term svg
	replot

Diese Ranges könnte man vor den Rest rausziehen, aber das ist mehr Arbeit als Nutzen. Fragen zum Script beantworte ich gerne.

Man benötigt Linux, bei dem die meisten Tools mit an Bord sind (tr, bash, sed, sort, tail) oder leicht nachinstalliert werden können (gnuplot, wget). Unter Windows ist es für jemanden, der nicht schon Erfahrungen mit Scripten hat, mühsam, alles zu installieren und einzurichten, aber möglich. Macs haben ja ein Open-BSD als Basis, inklusive Bash, da sollte es fast so leicht sein wie unter Linux.

Schon 8 Jahre nach der Erstveröffentlichung gibt es eine neue Version von fcd, dem schnellen Verzeichniswechsler für die Shell.

(Anm.: Mithilfe der Kommentare habe ich einen Bug gefunden, und in den Kommentaren wird auch erklärt, wie man ihn beseitigt. Ein Update des Artikels folgt daher in Kürze).

Die neue Version kommt mit Leerzeichen in Verzeichnisnamen zurecht – eine Funktion, die ich nicht direkt selbst brauche, da ich keine Verzeichnisse mit Leerzeichen mache, aber immer mehr Programme legen solche Verzeichnisnamen an (z.B. ‚VirtualBox VMs‘, Zoom: ‚Unknown Organization‘, …) und diese tauchen störend in den Auswahllisten auf.

Der Fix war überraschend einfach, nachdem ich mich erst mal zu der v.a. kosmetisch gedachten Verbesserung aufgerafft hatte.

Weiterlesen

 – Der Colordaemon war da! – 

colordaemon

Heute geht es mal wieder um Linux. Und nur am Rande um den Colordaemon, ein Daemon, also ein Hintergrundprozess, der sich, wie meist, durch ein kleines D am Namensende verrät. Wobei es auch ein, zwei Dämonen gibt, sich nicht so verraten, und natürlich gibt es Programme, die zufällig auf ‚d‘ enden, ohne dass sie damit signalisieren wollen, dass sie Dämonen sind. Also ein Fall, wie geschaffen, um den Unterschied zwischen starken Indizien von Beweisen zu erläutern, wüssten nicht mehr Leute, was Indizien und Beweise sind, als was Dämonen unter unixoiden Systemen sind.

Andere, mehr oder weniger bekannte Dämonen, sind der Druckerdaemon cupsd des common unix printing service, sowie dbus-daemon, avahi-daemon, die auf andere Weise ihre Dämonenhaftigkeit äußern, aber ich verrate jetzt nicht wie, usb-storage und cron sind wohl auch Dämonen, und haben kein D am Ende. Cron spielt noch eine Rolle am Rande. Dagegen wären sed und thunderbird Programme, die trotz D-Endung keine sind.

Wen’s interessiert, ps ax -o cmd gibt die Namen aller laufenden Prozesse aus.

Das eigentliche Thema hier sind aber weder Dämonen, noch der colord im Besonderen, sondern das syslog, Scheduler und Orage. Ein Scheduler ist ein Zeitplaner und Orage ist der Zeitplaner, um den es hier geht.

Motivation:

Oft wollen Leute, dass das System sie regelmäßig über Dinge informiert, und sie wollen es auf dem Desktop sehen. Dabei treffen sie auf Linuxexperten, die oft beruflich oder ehrenamtlich Sysadmin sind, und für solche Aufgaben die seit den 60ern bewährten Programme cron/anachron benutzen (anacron ist ein anachronististischer cron, habe ich mir sagen lassen), aber diese arbeiten befehlszeilenorientiert und werden über superschlanke, jedoch leicht kryptische Tabellen, die crontabs, konfiguriert – das ist schon nicht jedermanns Sache.

Außerdem laufen sie vom Systemstart weg, noch bevor die grafische Umgebung, X11 und der ganze Desktopkrempel geladen ist und nicht mit den Rechten des Users. Auf Servern läuft meist gar keine Desktopumgebung und folglich sind sie nicht sonderlich dafür ausgelegt, dem User, der womöglich gar nicht angemeldet ist, der auch kein DE (Desktop-Environment) geladen hat, eine Popupnachricht zu senden.

Es gibt dafür Lösungen, und zwar muss man die Xauthority bemühen und 0:0 an das Programm melden, welches um 17:15 aufpoppen soll – da man das so selten macht, hat man die Syntax nicht im Kopf und ist verstimmt. Im Ubuntuusers.de-Wiki ist, unter Fenster öffnet sich nicht, vermerkt wie es geht (noch etwas nach unten scrollen).

Auch ich habe schon oft darauf verwiesen, aber jetzt, bei meiner oberflächlichen Beschäftigung mit Orage eine einfache Alternative gefunden. Und darum geht es hier. :)

Also, wenn man ein Programm, dass die grafische Oberfläche braucht, starten will, dann geht man zu Orage. Hat man oben rechts in der Systemleiste eine Uhr laufen, dann bringt ein Doppelklick darauf wahrscheinlich Orage im Kalendermodus in den Vordergrund.

orage-monat

Was wir jetzt noch brauchen ist aber ein praktisches Beispiel, und deswegen kommen wir einfach nicht auf den Punkt, sondern holen nochmal auf einem zweiten Pfad aus.

Man hat ja unter /var/log/ jede Menge Logdateien liegen, die man nie anschaut, weil man sie nicht versteht. Geht was schief mit dem System wird man dann von naseweisen Linuxgurus gefragt, was das Syslog sagt. Der 0-8-15 User weiß nicht mal, dass es sowas gibt. Also ls -l /var/log/syslog eingeben:

> ls -l /var/log/syslog
-rw-r----- 1 syslog adm 71855 Feb 23 01:07 /var/log/syslog

Mit 70kb ein ganz schön fettes Ding.

Mit less /var/log/syslog (less is more) kann man sich den Inhalt ansehen. Wenn etwas schiefläuft, dann oft richtig, und die gleiche Nachricht taucht 100fach im Logfile auf. Mit einer Länge von 3 Zeilen a 150 Zeichen kommt man leicht auf 45.000 Zeichen. Die Nachrichten zu entziffern ist schon eine Wissenschaft für sich, aber wenn man regelmäßig reinschaut kann man ja einzelnen Nachrichten nach und nach auf den Grund gehen. Und man bemerkt vielleicht, wenn das Logfile plötzlich ganz anders ausschaut.

Früher hatte ich ewig, über unterschiedlichste Distributionen und Versionen hinweg Meldungen im Syslog, dass 3 Systemfonts nicht gefunden werden konnten. Im laufenden Betrieb hat sich aber nie ein Fehler geäußert. Einmal wollte ich sie loswerden und habe das Netz durchsucht, aber weder fand ich einen Ort, um die Fonts endlich runterzuladen, noch einen Trick die Meldungen loszuwerden.

Überflüssige Fehlermeldungen sind nämlich ein Problem: Sie erhöhen das Rauschen, den Heuhaufen, in dem man dann die Nadel nicht findet.

Ein anderer Unbill war eine fehlerhafte Druckerdatei, bei hoch gesetztem Loglevel des Druckerdaemons. Der hat dann gleich 100 Zeilen Fehlerprotokoll ins Log geschrieben, und das minutenweise. Das wurde rasch zu einem Dateigrößenproblem.

Jetzt gibt es auch den wunderbaren Daemon logrotate. Der rotiert das Logsystem, und zwar insofern, als die Logdatei von gestern zu xy.0.log umbenannt wird, die von gestern, die xy.0.log hieß wird xy.1.log genannt – sinnvollerweise in umgekehrter Reihenfolge, und so weiter, und die älteste wird gelöscht. Und was älter als gestern ist wird gezippt, und da diese Logdateien oft hochgradig redundant sind, sind die gezippten Dateien oft wesentlich kleiner, als die frischen, ungezippten. Weil sysadmins so oft in diese Dateien schauen müssen, gibt es extra das Programm zless, dass ein less auf gezippte Texte ausführt.

Wir beenden den Exkurs zu den Logdateien und kommen zurück zum Thema. Als auf Stackexchange jemand fragte, wie ein grafisches Programm zeitgesteuert startet – er wollte etwas ähnliches wie Logdateien ansehen, dachte ich nach, wie ich das lösen würde, aber konnte ihm nicht konkret helfen, weil er eine Lösung für MacOS sucht. Aber so kam ich auf die Idee, 1x täglich könnte ich mir die Syslogdatei vorlegen, und wollte sehen, ob sich das nicht mit Orage lösen lässt, wo ich bemerkt hatte, dass man zur Benachrichtigung ein Programm starten kann.

Dieses Programm kann man leicht selbst schreiben, und von Orage starten lassen. Die Idee ist dabei, die oft ellenlange Ausgabe der Logdatei zu kürzen, so dass ähnliche Meldungen nur noch einmal auftauchen. Meine Logdatei zeigt etwa sowas an:

Feb 22 07:35:02 tux201t colord[1154]: (colord:1154): Cd-WARNING **: failed to get session [pid 9723]: Kein passendes Gerät bzw. keine passende Adresse gefunden
Feb 22 07:35:02 tux201t colord[1154]: message repeated 5 times: [ (colord:1154): Cd-WARNING **: failed to get session [pid 9723]: Kein passendes Gerät bzw. keine passende Adresse gefunden]
Feb 22 07:35:10 tux201t anacron[9507]: Job `cron.daily' terminated
Feb 22 07:35:10 tux201t anacron[9507]: Normal exit (1 job run)
Feb 22 07:39:33 tux201t kernel: [1253876.380868] sd 6:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_SENSE
Feb 22 07:39:33 tux201t kernel: [1253876.380875] sd 6:0:0:0: [sdb] tag#0 Sense Key : Hardware Error [current] [descriptor]
Feb 22 07:39:33 tux201t kernel: [1253876.380877] sd 6:0:0:0: [sdb] tag#0 Add. Sense: No additional sense information
Feb 22 07:39:33 tux201t kernel: [1253876.380882] sd 6:0:0:0: [sdb] tag#0 CDB: ATA command pass through(16) 85 06 20 00 00 00 00 00 00 00 00 00 00 00 e5 00
Feb 22 07:49:01 tux201t CRON[10242]: (stefan) CMD (date >> $HOME/foo.dat)
Feb 22 07:49:33 tux201t kernel: [1254476.385144] sd 6:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_SENSE
Feb 22 07:49:33 tux201t kernel: [1254476.385154] sd 6:0:0:0: [sdb] tag#0 Sense Key : Hardware Error [current] [descriptor]
Feb 22 07:49:33 tux201t kernel: [1254476.385160] sd 6:0:0:0: [sdb] tag#0 Add. Sense: No additional sense information
Feb 22 07:49:33 tux201t kernel: [1254476.385167] sd 6:0:0:0: [sdb] tag#0 CDB: ATA command pass through(16) 85 06 20 00 00 00 00 00 00 00 00 00 00 00 e5 00
Feb 22 07:59:33 tux201t kernel: [1255076.377070] sd 6:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_SENSE
Feb 22 07:59:33 tux201t kernel: [1255076.377081] sd 6:0:0:0: [sdb] tag#0 Sense Key : Hardware Error [current] [descriptor]
Feb 22 07:59:33 tux201t kernel: [1255076.377086] sd 6:0:0:0: [sdb] tag#0 Add. Sense: No additional sense information
Feb 22 07:59:33 tux201t kernel: [1255076.377093] sd 6:0:0:0: [sdb] tag#0 CDB: ATA command pass through(16) 85 06 20 00 00 00 00 00 00 00 00 00 00 00 e5 00
Feb 22 08:00:01 tux201t CRON[10538]: (stefan) CMD (/home/stefan/bin/blogcounter.sh -w >> /home/stefan/.bloglog)
Feb 22 08:09:33 tux201t kernel: [1255676.380279] sd 6:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_SENSE
Feb 22 08:09:33 tux201t kernel: [1255676.380286] sd 6:0:0:0: [sdb] tag#0 Sense Key : Hardware Error [current] [descriptor]
Feb 22 08:09:33 tux201t kernel: [1255676.380289] sd 6:0:0:0: [sdb] tag#0 Add. Sense: No additional sense information
Feb 22 08:09:33 tux201t kernel: [1255676.380293] sd 6:0:0:0: [sdb] tag#0 CDB: ATA command pass through(16) 85 06 20 00 00 00 00 00 00 00 00 00 00 00 e5 00
Feb 22 08:17:01 tux201t CRON[11053]: (root) CMD ( cd / && run-parts --report /etc/cron.hourly)
Feb 22 08:17:01 tux201t cron[926]: Please install an MTA on this system if you want to use sendmail!
Feb 22 08:17:01 tux201t CRON[11052]: (root) MAIL (mailed 107 bytes of output but got status 0x00ff from MTA#012)

Bis auf die Uhrzeit wiederholt sich vieles, und bis auf die magischen Zahlen in Backsteinklammern.

Das kann man mit sed, dem StreamEDitor, rausfiltern. Außerdem brauche ich die Angabe ‚tux21t‘, meinen Rechnernamen nicht – da ist nur der eine.

​​​​​​​sed -r 's/.* tux201t//;s/kernel: .[0-9.]+. /kernel: /;s/CRON.[0-9]+./CRON/' /var/log/syslog

colord[1154]: (colord:1154): Cd-WARNING **: failed to get session [pid 9723]: Kein passendes Gerät bzw. keine passende Adresse gefunden
colord[1154]: message repeated 5 times: [ (colord:1154): Cd-WARNING **: failed to get session [pid 9723]: Kein passendes Gerät bzw. keine passende Adresse gefunden]
anacron[9507]: Job `cron.daily' terminated
anacron[9507]: Normal exit (1 job run)
kernel: sd 6:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_SENSE
kernel: sd 6:0:0:0: [sdb] tag#0 Sense Key : Hardware Error [current] [descriptor]
kernel: sd 6:0:0:0: [sdb] tag#0 Add. Sense: No additional sense information
kernel: sd 6:0:0:0: [sdb] tag#0 CDB: ATA command pass through(16) 85 06 20 00 00 00 00 00 00 00 00 00 00 00 e5 00
CRON: (stefan) CMD (date >> $HOME/foo.dat)
kernel: sd 6:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_SENSE
kernel: sd 6:0:0:0: [sdb] tag#0 Sense Key : Hardware Error [current] [descriptor]
kernel: sd 6:0:0:0: [sdb] tag#0 Add. Sense: No additional sense information
kernel: sd 6:0:0:0: [sdb] tag#0 CDB: ATA command pass through(16) 85 06 20 00 00 00 00 00 00 00 00 00 00 00 e5 00

Das ist kürzer, und jetzt sind viele Zeilen identisch. Mit | sort | uniq -c | sort -r -n kann man die Ausgabe sortieren, mit uniq die wiederholten Zeilen löschen, uniq -c zählt dabei die gelöschten Duplikate, ein weiteres sort -r -n sortiert dann numerisch und reverse, das heißt hier absteigend.

110 kernel: sd 6:0:0:0: [sdb] tag#0 Sense Key : Hardware Error [current] [descriptor]
110 kernel: sd 6:0:0:0: [sdb] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_SENSE
110 kernel: sd 6:0:0:0: [sdb] tag#0 Add. Sense: No additional sense information
109 kernel: sd 6:0:0:0: [sdb] tag#0 CDB: ATA command pass through(16) 85 06 20 00 00 00 00 00 00 00 00 00 00 00 e5 00
18 CRON: (stefan) CMD (/home/stefan/bin/blogcounter.sh -w >> /home/stefan/.bloglog)
18 CRON: (stefan) CMD (date >> $HOME/foo.dat)
18 CRON: (root) MAIL (mailed 107 bytes of output but got status 0x00ff from MTA#012)
18 CRON: (root) CMD ( cd / && run-parts --report /etc/cron.hourly)
18 cron[926]: Please install an MTA on this system if you want to use sendmail!
8 dbus[986]: [system] Activating via systemd: service name='org.bluez' unit='dbus-org.bluez.service'
7 org.freedesktop.Notifications[1801]: /home/stefan/.cache/xfce4-notifyd-theme.rc:1: error: scanner: unterminated string constant

Jetzt wollen wir noch die Ausgabe an ein simples Grafikprogramm weitergeben. Hier bietet sich Zenity an, welches bei Xubuntu dabei ist – eine Alternative wäre xdialog – das wir mit der Größe 1400×600 starten, und für das wir ein paar Label festlegen.

​​​​​zenity --list --text="Syslog messages" --column="logmsg" --width=1400 --height=600

Zenity war hier schon Thema und soll nicht näher besprochen werden. man zenity oder zenity --help verraten das wichtigste in Kürze. Auch im Ubuntuusers.de führt in den Umgang ein.

Komplett sieht unser Programm also jetzt so aus:

#!/bin/bash
#

condense syslog ((c) GPLv3)

#
sed -r 's/.* tux201t//;s/kernel: .[0-9.]+. /kernel: /;s/CRON.[0-9]+./CRON/' /var/log/syslog \
| egrep -v "Starting Hostname Service.|Started Hostname Service" | sort | uniq -c | sort -nr | \
zenity --list --text="Syslog messages" --column="logmsg" --width=1400 --height=600

Aufmerksame Leser haben bemerkt, dass sich hier noch ein Kommando eingeschlichen, hat, und zwar egrep -v "A|B" . Mit egrep -v kann man unerwünschte Zeilen ausfiltern, die ein Schlüsselwort enthalten. Diese zwei, durch ein Pipesymbol getrennte Namen A|B sind als A oder B zu lesen, und sind nach Bedarf zu erweitern, A|B|C|D … – so kann man Meldungen, die nur Erfolg verkünden, ausblenden. Wir wollen ja die unangenehmen Heinis identifizieren. Mit der Zeit wächst das sicherlich an, wenn man diszipliniert nach und nach allen Meldungen auf den Grund geht.

Weil die Befehlskette so lang ist, habe ich sie umgebrochen. Dazu benutzt man den Backslash, dem sofort ein Zeilenumbruch folgen muss, dann in der nächsten Zeile fortgesetzt.

Ich nenne sie syslog-view.sh, speichere sie in meinem bin-Dir ~/bin, welches im Pfad ist, und markiere sie mit chmod a+x ~/bin/syslog-view.sh als ausführbar.

Mit syslog-view.sh kann man sie jetzt aufrufen und sollte das auch testen. Bei Fehlern testet man sinnvollerweise erst die Filterung mit sed, dann die sort-Kette in Verbindung, schließlich ob man Zenity mit einer anderen Eingabe so aufrufen kann, und dann die Kombination.

Jetzt kommen wir zum eigentlichen Punkt, den Aufruf durch Orage. Hier sind jetzt die GUI-Freunde in ihrem Element. Wir klicken einen einzelnen Tag an, und kommen zum Tagesfenster. Da wählen wir Ereignis oder Aufgabe – der Unterschied hat sich mir noch nicht erschlossen, und dann, links unter Datei, das Symbol für Neu, das Blatt mit dem Plus.

orage-tagesfenster

Wenn wir schwach sind im Entscheiden, dann können wir hier wieder von Ereignis zu Aufgabe wechseln oder zurück. Ich war für Ereignis. Da vergeben wir einen Titel. Ort hat wenig Sinn, aber ich bin kein Orage-Crack, womöglich entgeht mir da was. Und eine Uhrzeit legen wir fest – 18:05 halte ich für eine excellente Entscheidung. Oft bin ich da schon wach.

orage-ereignis-neu1

Rubriken sind wohl interessant, wenn man viel mit Orage verwaltet. Priorität habe ich so gelassen und alles. Dauer 30 Minuten steht auch nur so da.

Nächster Reiter, Erinnerung, es wird spannend! orage-erinnerung1

Mit 0:0:0 als Erinnerungszeitpunkt verraten wir Orage, dass die Anwendung selbst die Aufgabe ist. Wenn wir ins Konzert wollen, und eine Stunde früher los müssen, dann würden wir da wohl 1 Stunde eintragen. Wir sind von der Sorte hier-und-jetzt. Anwendung wollen wir benutzen, und tragen den Pfad zum Programm und Programmnamen ein. Ob Shellkürzel wie $HOME oder ~/ funktionieren würden habe ich nicht getestet.

Nächster Reiter, Wiederholung:

orage-wiederholung1

Alle Stunde ist wohl etwas oft. Das hatte ich zum Testen eingestellt. Täglich, wöchentlich, monatlich und jährlich stehen noch zur Auswahl, und man könnte auch 2-wöchentlich oder sowas festlegen. Komplexität bitte selbst erforschen.

Tja, und jetzt poppt täglich mein Logmelder auf, und ich darf mich fragen, was der Colordämon wohl ist, und was er macht. Das Ziel ist es, nach und nach den Meldungen auf den Grund zu gehen, diejenigen zu beseitigen, die ich beseitigen kann, und diejenige auszufiltern, die harmlose Statusmeldungen sind. So dass das Logfile leerer wird, und neue Fehler dann auch auffallen.

Achso – wie sieht es in Zenity aus?

zenity-logview

Die Überschrift, man solle was auswählen, kommt vom Listenmodus zenitys. Womöglich kann man sie durch was eigenes ersetzen. Ob man OK oder Abbrechen drückt hat den gleichen Effekt – das Fenster schließt sich. Man könnte bei OK die Ausgabe auffangen und damit was anstellen (automatisierte Googlesuche, etwa, oder Frage eröffnen bei Ubuntuusers.de, oder Email an den persönlichen Linuxguru schicken.)

Ich hatte mal einen Nullmailer installiert; das wozu ist eine andere Geschichte, die hier nicht erzählt werden soll. Dann klappte es nicht mehr, wurde aber auch nicht mehr gebraucht. Daher jedenfalls stammt dieser Eintrag mit 18 Meldungen, ich solle einen MTA (Mail transport agent) installieren. Zenity kann man, meine ich, mitteilen, ob man die Zeilen edieren darf. Ja, das werde ich noch einstellen, wenn Hinweise in den Kommentaren nicht schneller sind, denn so, wie es jetzt ist, kann man schlecht den Text kopieren, um ihn selbst ins Google-Suchfeld einzugeben.

 

– WordPress Blogchart –

feininger-stats

Dieser Beitrag stützt sich auf den ersten Schritt, die verschwundene Ministatistik aus WordPress zurückzuholen bzw. Ersatz dafür zu schaffen. Ziel war es, ein kleines, grafisches Diagramm zu erzeugen, das man in das Menüpanel von XFCE4 wie es bei Xubuntu zum Einsatz kommt, integriert – mit möglichst wenig Arbeit, geringem Ressourcenverbrauch und Tools, die ich eh so rumliegen habe.

(Update: Bugfix und Erweiterung, Nachtrag)

Teilweise ist das auch gelungen.

Es gibt ein praktisches Grafikformat, XPM, bei dem man eine Textrepresentation der Grafik mit ein wenig Syntax dekoriert, und fertig ist das Bild. Wird von sehr vielen Programmen verstanden, ist prima um zu kontrollieren, ob alles an seinem Platz ist, auch aus einem Textfenster heraus, und es kann einigermaßen per Hand erzeugt werden, umso besser je kleiner es ist und je  weniger Farben man hat. Klein und schwarz/weiß sollte es eh werden – dafür könnte man auch XBM nehmen, aber das macht kaum einen Unterschied, nur dass der XBM-Eintrag bei Wikipedia nur eine Wurzel ist.

Man erinnert sich – meine Blogstatistik liegt als ~/.bloglog in solchen Daten vor:

185451 8 8 59 20:00 2017-01-10
185455 4 3 60 21:00 2017-01-10
185455 0 0 60 22:00 2017-01-10
185457 2 2 60 23:00 2017-01-10

und ich wollte die letzten 48 Stunden abbilden, also von den letzten 48 Zeilen der Datei die Spalte 2. Meine naive Idee war es, irgendein Icon in der Titelleiste von XFCE, dem Desktop von Xubuntu, einzubinden, und dieses rhythmisch mit einer selbstgemachten Grafik zu überschreiben. Im original Iconpath hat der ordinäre User keine Schreibrechte. Die Lösung ist dann nicht, sich solche Rechte einzuräumen. Als isolierte Lösung, die man nur selbst verwendet wäre das vielleicht kein Problem, aber vielleicht rollt das nächste Update die Rechte zurück (eher nicht). Oder man verbloggt die Lösung, sie greift um sich und schwupp-di-wupp ist eine Sicherheitslücke gerissen. Man kann aber einen Starter im XFCE-Menü anlegen, verknüpft ihn mit einem Programm das gar nichts macht, und ordnet diesem ein Icon zu, das man da platziert, wo man selbst Schreibrechte hat, also unter /home/saskia oder wie man gerade heißt.

Das funktionierte als Schritt eins, aber dann war Schluss mit lustig. Erst erhöhte ich die Höhe meiner Leiste von 24 auf 32 Pixel, weil 32 größer ist als 24. :) Dann machte ich aus der Kopie irgendeiner XPM-Datei eine Schwarz-Weiß-Grafik, was auch gelang, aber Änderungen an dieser wurden nicht bemerkt. Man musste die Eigenschaft des Starters aufrufen, dann, ohne was zu ändern „Speichern“ drücken, und erst dann wurde die geänderte Grafik auch verändert angezeigt.

toxic-stats

Und das zweite Ärgernis: Eine 3232 Grafik wurde schön angezeigt, aber eine 3248 Grafik nicht, wobei ich zwischenzeitlich noch in die übliche Grafikformatfalle tappte, x und y zu vertauschen – bei quadratischem Ausgangsmaterial passiert das leicht, und aus dem Matheunterricht kennt man das: erst die x-Achse, dann y. X ist ja auch im Alphabet vor Y. In der Textwelt ist es aber meist: Erst Zeile, dann Spalte – auch der Röhrenfernseher baut das Bild zeilenweise auf, so wie man einen Text schreibt. Physiker! Dann gibt es noch die Anomalie, dass der 0-Punkt oben liegt und nach unten die Werte größer werden – sehr unmathematisch das Ganze.

Also das 32*48 Pixelbild wurde schon angezeigt, aber so gestaucht, dass die 48er Breite auf 32 runtergerechnet wurde, so dass die Höhe nur noch 2/3 von 32 betrug, also ca. 20 Pixel, und da gehen natürlich manche Säulen des Säulendiagramms verloren.

blogchart

32*48

Jetzt gibt es viele Plugins, die alles mögliche in die Kopfzeile, die man auch an den Fuß des Bildschirms legen kann, malen, etwa eine CPU-Überwachung, die sogar sekündlich aktualisiert wird. Das sollte doch irgendwie gehen. Ich versuchte rauszufinden, wie solche Plugins heißen, was leider nicht so trivial ist. Die meisten tauchen in der Konfigurationsliste mit einem Namen auf, zu dem man kein Programm findet, weil die Liste übersetzt ist, während die Programmnamen englisch sind.

Dann hatte ich mit Pythonskripten gerechnet, weil allüberall die Linuxer gerne Python machen und Python ist ja auch nicht verkehrt, verglichen mit anderen P-Sprachen, wie PHP oder Perl, was auf dem Server manchmal eingesetzt wird, wobei Perl eigentlich ein Kuhdorf, 1,6km von Apach entfernt ist, und Apach ist kein Webserver, aber das führt hier zu weit. Nur wo wir dabei sind: Wer da wohnt, der sollte doch mal ein Foto machen von einem Bus vor dem Ortschild von Perl, und auf dem Bus müsste „Apach“ stehen, und man müsste es so beschneiden, dass der Betrachter glauben kann, da folge noch ein e. Oder umgekehrt vor dem Ortschild von Apach mit einem Bus nach Perl.

Zurück zum Thema: Ich fand also plugins, aber das waren .so-Dateien. Das deutet auf compilierten C- oder C++-Code hin, Sprachen, die ich zwar mal gelernt habe, aber die sind sehr systemnah, und bei Fehlern schmiert einem das halbe System ab.

Also kurz gegoogelt, und die Lösung rasch fast auf dem Präsentierteller bekommen: Das XFCE4-Genmon-Plugin. Mon für montags… – für monitoring, und gen für generisch. Eine Anleitung gefunden, auch hier wieder gesucht, wie das Teil auf deutsch heißt, ob es vielleicht schon unbemerkt installiert ist (ist es nicht), installiert sowie auch ein paar fertige Beispielexemplare, die ich dann aber gar nicht angeschaut habe, Befehl dazu:

apt-get install xfce4-genmon-plugin xfce4-goodies

Da haben sich gleich verschiedene Lösungen angeboten. Ich dachte, eine kl. Grafik, 32×48 will ich und etwas Text, nämlich den Maximalwert der letzten 48 Stunden und den letzten Messwert, der, da am Rand, oft schlecht zu sehen ist, und im Falle eines Ausreißers mit sagen wir 200 Abrufen, da werden die Striche so klein, dass man schlecht noch abschätzen kann, ob das jetzt 10 oder 20 Abrufe waren, und müsste 48 Stunden warten, bis der Peak ausgewandert ist.

Man braucht einfach nur ein Script zu schreiben, das eine XML-Datei erzeugt, und diese verweist auf eine Grafik, die entsprechend der Einstellungen aktualisiert wird. Mein Script nannte ich blogchart.sh und legte erst 3s fest, um schnell zu sehen, ob sich was tut, dann später 30s, damit ich Zeit hatte, die vom Script erzeugte Grafik noch zu sichern, bevor der nächste Lauf sie überschreibt, und am Ende 3600s, weil das eine Stunde ist, und sich so oft mein .bloglog ändert.

  • Befehl: blogchart.sh
  • [ ] Beschriftung:
  • Intervall(s): 3600
#!/bin/bash
#
#
# Erweiterung wird unten wechselnd angehängt (xpm/png) 
img=/home/stefan/.blogchart
# Bei mehr oder weniger als 48 Stunden hier anpassen (aber auch in der Funktion asciiart):
views=($(tail -n 48 ~/.bloglog | awk '{print $2}' | column -x))
# Debug:
# views=(5 4 10 8 6 6 5 4 4 3 5 0)

Der Bildnamensvorname mit Pfad taucht öfter auf, also legt man eine Variable an, um es später kurz zu haben.

Views nimmt die letzten 48 Zeilen aus ~/.bloglog und awk filtert daraus die die 2. Spalte. Das ganze wird von column dann in eine Serie von Werten nebeneinander gestellt, statt in 48 Zeilen, was beim Debugging störend ist. Die runden Klammern mit dem $ sorgen dafür, dass die Ausgabe des Befehls ausgewertet werden kann, und die runden Klammern darum wieder, machen aus den Werten ein Array, so wie zwei Zeilen tiefer zu sehen, nur dass es da nicht 48 sind. Es ist auch störend, wenn man was berechnet hat beim Debuggen und sich an Werte gewöhnt hat, und dann sind 60 Minuten um und man hat ganz andere Werte.

max () {
 maximum=0
 for view 
 do
   test $view -gt $maximum && maximum=$view
 done
 echo $maximum 
}
m=$(max ${views[@]})
# Avoid Div/0:
test $m -eq 0 && m=1

Jetzt brauchen wir das Maximum und – man glaubt es nicht – die Bash hat keine fertige Maximumsfunktion! Gut – so oft braucht man die auch nicht; ich habe nämlich auch keine.

Diese Form der For-Loop ist etwas überraschend. Normal sieht man sowas wie:

  • for obst in apfel birne banane
  • for i in 3 4 7
  • for (( x=0;  x<10; x+=1 ))

aber hier – woher? Nun, von den Funktionsparametern bzw. hier dem Array, das ich oben definiert habe und mit dem ich die Funktion aufrufe, also den Abrufzahlen pro Stunde.

Für die Debugwerte sollte das 10 ergeben und tut es auch. max ${views[@]} ist der Funktionsaufruf mit dem kompletten Array (@), m=$(…) die Zuweisung der Ausgabe (deswegen echo) an die neue Variable m. Bitte keine Backticks verwenden!

Den Maximalwert brauchen wir aus zwei Gründen: Einmal um ihn anzuzeigen, einmal um die Messwerte so zu strecken, dass der Maximalwert genau die volle Höhe einnimmt, und die anderen Werte werden dann so umgerechnet, dass diese proportional dazu kleiner sind, bis auf Rundungsfehler.

Das macht meine Funktion auf32:

auf32 () {
 m=$1
 shift
 # echo Parameter: $#
 # Bei anderer Imagehöhe als 32 hier anpassen: 
 for hour
 do
   echo -n $((hour*32/m))" "
 done
 echo 
}

normalized=$(auf32 $m ${views[*]})

Klein m ist der Maximalwert, den ich als ersten Parameter der Funktion übergebe und da auch genauso benenne. Man könnte auch von der inneren Funktion auf die äußere, bereits definierte Variable zugreifen, aber globale Variablen sind bekanntlich von Übel. Das shift schiebt alle Parameter um eins nach vorne, hat man den ersten noch nicht ergriffen ist er jetzt endgültig weg. Jetzt ist also der m-Wert fort und nur noch das Array bildet die Parameter.

Was ich eben noch view nannte, und in der Einzahl kein guter Name ist, aber views war schon belegt, nenne ich hier hour. Gemeint sind die views per hour, also 5, 4, 10 nach den Testwerten. Mit $((…)) kann man in der Bash etwas Ganzzahlarithmetik betreiben. Mathematiker vorsicht! hour32/m ist hier nicht das gleiche wie hour/m32. Hour ist maximal so groß wie m, viele Werte sind kleiner. 10/10 ist also 1, alle anderen Werte sind kleiner/gleich 1 aber größer/gleich 0, wenn kein Fehler aufgetreten ist, und werden zu 0 abgeschnitten. Nicht gerundet – abgeschnitten. 0,99 wird 0. Und das kann man gerne mit 32 multiplizieren, das bleibt 0. Aber multipliziert man zuerst, dann wird aus 5, 4, 10 – ähm, (128+32=160), 128, 320. Das jeweils durch 10 sind 16, 12, 32. So soll das sein. Wer unbedingt runden will addiert zwischendrin 5, also (hour*32+5)/m, Punkt vor Strich akzeptiert die Shell, ansonsten von links nach rechts.

Außer m wäre Null, das könnte bei schlechtbesuchten Blogs ja in 48 Stunden mal vorkommen, daher oben die Sicherung für Div/0-Fehler.

Das echo -n unterdrückt die neue Zeile nach echo, die Leerstelle muss sein, damit die Werte nicht aneinanderkleben (161232). Das Ergebnis speichere ich in normalized. Diesmal benutze ich den * für das ganze Array:

${views[*]}

nicht das @, wieso weiß ich nicht, eine Laune, hier macht es keinen Unterschied. Wo macht es einen Unterschied? Ich glaube bei Strings mit Leerstellen:

t201:~/bin > namen=(a "b b" ccc)
t201:~/bin > for s in "${namen[@]}"; do echo $s"-"; done 
a-
b b-
ccc-
t201:~/bin > for s in "${namen[*]}"; do echo $s"-"; done 
a b b ccc-

Macht das Sinn? Nein. Kann man sich das merken? Nein. Man kann es nur rasch ausprobieren, dann weiß man es wieder. Oder man benutzt es so oft, dass man es automatisch lernt. Sich hinsetzen und sowas lernen, ohne es sicher zu brauchen, da vermüllt man sich das Hirn. Das Bashmanual kann man auch nicht lesen – es ist zu lang, und da gibt es noch mehr solcher Merkwürdigkeiten, etwa 3 unterschiedliche ifs.

In normalized steht jetzt also (16 12 32 …). Prima. Wir kommen zur letzten Funktion, dem 2.letzten Block und 3.letzten Thema. Wir lassen nämlich das XML der Funktion erst weg und konzentrieren uns aufs grafisch/mathematische.

asciiart () {
 # Und hier Bildhöhe anpassen, Pixelzahl
 for row in {32..1}
 do
   echo -n '"'
   for traffic in ${normalized[@]}
   do
     test $traffic -ge $row && echo -ne "* " || echo -ne "  " # 2 Leerzeichen
   done
   echo '",'
 done
}

Hier wurde ich der Devise mit den globalen Variablen untreu, wie mir gerade auffällt (normalized) aber der Reihe nach. {32..1} erzeugt einfach eine Serie Zahlen von 32 bis 1 und merkt von selbst, dass es rückwärts geht, also 32 31 30 29 … 3 2 1 wird hier erzeugt. Für jede Zeile der Asciiartdatei gebe ich Gänsefüßchen aus. Und dann laufe ich durch meine normalisierten Werte und schaue, ob sie größer oder gleich der Zeilenzahl sind. Wenn ja gebe ich ein „* “ aus, sonst “  “ (2 Leerzeichen). Statt -ne müsste bei echo eigentlich -n reichen – e ist für extended characters oder sowas. Und nach jeder Zeile Gänsefüßchen zu, und Komma.

Jetzt müssen wir uns das XPM-Format genau ansehen. Es gibt einige Freiheiten, die man selbst nachschlagen kann. Ich erkläre nur das nötigste.

Es muss mit diesem XPM in einem C-artigen Kommentar beginnen (s.u.). Anfangs hatte ich darum aus Nachlässigkeit auch Gänsefüßchen – ein schwer zu findender Fehler! Dann eine C-style Stringdefinition. Muss man nicht kennen, kann man Zeichen für Zeichen übernehmen. Dann kommt, in Gänsefüßchen, Breite, Höhe (4832), Zeichenlänge pro Pixel (2) und Anzahl der Farben (2). Als Zeichenlänge würde 1 natürlich genügen für 0 oder 1, schwarz oder weiß, aber da die Schriftzeichen bei Bildschirmschrift ca. doppelt so hoch wie breit sind wirkt dann alles gestreckt. 2 ist also auch für uns nicht verkehrt. Unser Hintergrund soll weiß sein, das ist „. „-codiert (“  „, die Balken schwarz, dafür nehme ich ein „M “ („ „, das ziemlich viele Pixel bedeckt. Keine schlechte Idee, außer um darüber bei WordPress zu berichten, welches mehrere Leerzeichen in Folge eindampft – in Wirklichkeit habe ich zwei Leerzeichen und ein Sternchen benutzt aber das ist normalerweise ziemlich wumpe. Man hätte auch zwei M verwenden können – immerhin haben wir gesagt, Zeichenlänge 2, das ergibt bei 80 legalen Zeichen (a-zA-Z0-9 und einigen Sonderzeichen) 80*80 darstellbare Farben. Eine Darstellungsoption ist die bekannte Hexcodierung #(rr)(gg)(bb) für die rot/grün/blau-Werte von 00 über 99 bis ff. Das kleine M in den zwei Farbkodierungen steht für monochrom aber auch c sollte hier funktionieren für color.

Wie Sie sehen sehen Sie nichts. Ja doch, aber nicht 48 Spalten und 32 Zeilen – ich habe es gekürzt, der Übersichts halber, und zähle jetzt nicht nach, wie breit und hoch das Bild wirklich ist. Nach dem 5zeiligen Header folgen dann die Bildpunkte als Text. Übrigens wurde ja schon angedeutet, dass vielfarbige XPMs gibt und das System mit einigen schon ankommt. Oft wird die Zeichenkodierung so gewählt, dass Buchstaben mit vielen Pixeln auch entsprechend dunklen Farbwerten zugeordnet sind, bzw. man erzeugt die  Grafikdateien programmatisch. So kann man sich einige ansehen:

cat $(locate er.xpm) | less

aber das nur am Rande. XPMEXT bedeutet, dass es XPM-Extensions am Ende der Datei gibt, die muss man deklarieren, wie Kippen beim Zoll, wenn man sie nutzen will.

 

/* XPM */
static char *XPM_example[] = {
"48 32 2 2 XPMEXT",
". m #ffffff",
"M m #000000",
". . . . . . . . . M . . . . . . ",
". . . . . . . . . M . . . . . . ",
". . . . . . . . . M . M . . . . ",
". . . . . . . . . M . M . . . . ",
". . . M . . . . . M . M . . . . ",
". . . M . . . . . M . M . . . . ",
". . . M . . . . . M . M . . . . ",
". . . M . . . . . M . M . . . . ",
". . M M . . . . . M . M . . M . ",
". . M M M . . . . M . M M . M . ",
". . M M M . M M . M . M M M M . ",
". . M M M . M M . M . M M M M . ",
". . M M M M M M . M . M M M M . ", 
"XPMEXT author demystifikation.wordpress.com GPLv3",
"XPMENDEXT"
};

Jetzt sehen wir auch die Extensions. Wie man sieht überflüssiger Kokolores, aber er ist aus einem Grund praktisch. Später dazu.

Hier der Code im ganzen, der das XML erzeugt:

asciiart () {
 echo -e '/* XPM */'
 echo -e 'static char *XPM_example[] = {'
 # Erste Zahl ist Breite, zweite ist Höhe, also 48 32 für 2 Tage bei 32 Pixeln, sonst hier anpassen
 echo -e '"48 32 2 2 XPMEXT",'
 echo -e '"  m #ffffff",'
 echo -e '"* m #000000",'
 # Und hier Bildhöhe anpassen, Pixelzahl
 for row in {32..1}
 do
   echo -n '"'
   for traffic in ${normalized[@]}
   do
     test $traffic -ge $row && echo -ne "* " || echo -ne "  " # 2 Blanks!
   done
   echo '",'
 done
 echo '"XPMEXT author demystifikation.worpress.com GPLv3",'
 echo '"XPMENDEXT"'
 echo '};'
}

Oben Ausgabe des Headers, dann die besprochenen For-Loops, dann die extension die auch explizit und so beendet werden muss. Das Problem hier wäre, dass man ohne Extension nach dem letzen Anführungszeichen kein Komma braucht, um gültigen C-Code zu erzeugen, ja dass ein abschließendes Komma vor der geschweiften Klammer-Zu im XPM ungültig wäre. Das könnte man mit einer if-Abfrage abfangen, aber eine Abfrage, die 32x geprüft wird, um nur 1x anzuschlagen – das ist irgendwie eklig. Eigentlich nicht schlimm – der Prozessor rechnet das gerne und als es ins Gewicht fiel habe ich noch nicht gelebt, aber ich kann nicht anders. Es ist zusätzliche Komplexität, und Komplexität ist von Übel. Man kann nicht jede Komlexität vermeiden und sollte auch nicht, so einfach ist es auch nicht. Nein, es ist selbst wiederum komplex. Also wenn so eine Extension noch nachkommt, dann schadet das Komma nicht in der Loop sondern ist sogar nötig, und dafür lassen wir es in der letzten Zeile ganz unauffällig weg, ohne ein if zu benötigen.

Dennoch baut man ja bei solchen Strings, die man ausgibt, und die selbst der Maskierung bedürfen (Apostrophe) und Syntax klammern, die in Sonderfällen und Ausnahmen und Ausnahmen von den Ausnahmen – kurz und gut, mein Ergebnis war erst schrottig, dann wollten manche Programme das XPM anzeigen, andere nicht, dann war es ganz schrottig, dann wieder besser und man sucht sich einen Wolf. Man sollte mal einen XPM-Validator schreiben, denn ich habe zwischendurch einen gesucht, aber im Netz keinen gefunden.

Gimp wollte meine XPM-Files nicht öffnen, display, identify und andere Tools aus ImageMagick schon, der EOG-Viewer nicht aber Ristretto oder war es umgekehrt? Xpmtoppm hat auch nicht geklagt aber die, die rumgezickt haben, haben meist nicht verraten an welcher Stelle in der Datei sie stolpern. Einmal hieß es „zu wenig Pixeldata“ aber das war falsch. :)

Aber statt Testcases zu schreiben kann man auch über die Entwicklung bloggen. Da denkt man ja schneller, als man schreiben kann, und hat dann pro Satz ½ Satz Zeit, sich mit dem Inhalt zu befassen, über den man eigentlich gerne schnell wegginge, und in diesen Pausen findet man dann die Fehler. 3 Stück habe ich gefunden. Ich sollte gleich ein Buch schreiben und Seminare anbieten, BDD, blog-driven-development, der neue, heiße Scheiß!

Aber vorher binden wir noch Zutaten zu einem Menü zusammen, husch, husch!

# echo ${views[@]}
# echo $m
# echo ${normalized[*]}

asciiart > $img.xpm
# convert $img.xpm $img.png 
# n=${#views[*]}
# echo "<txt>Max: $m Last: ${views[n-1]}</txt>"
# übler hack, hier auf row aus der Schleife der Funktion zuzugreifen. 
echo "<txt>Max: $m Last: $row</txt>"
# echo "<img>$img.png</img>"
echo "<img>$img.xpm</img>"
echo "<tool>views: ${views[@]} normalized: ${normalized[@]} </tool>"

Ein paar auskommentierte Debugmeldungen, dann der große Moment, Aufruf von asciiart, Ausgabe der XPM-Datei nach $img.xpm. Zwischenzeitlich habe ich, weil das Menü mein xpm nicht fressen wollte, dieses mit convert, das mehr gierig als pedantisch ist, in png gewandelt, und das dann angezeigt. Jetzt , dank gefundener Fehler, geht auch xpm. Weitere Debugmeldungen, auskommentiert. Dann geht es weiter mit XML, wie es GenMon erwartet. Txt ist Text, der ausgegeben wird, im Bild unten Max: 24 Last: 11. Kommt auch von den Säulenhöhen hin. Dann mit img die eben erzeugte xpm-Datei, also der Pfad/Name dahin. Dann ein optionaler Tooltiptext. Auch mehr zu Debugzwecken.

blogchart

Zusätzlich kann man noch definieren, was bei Klick auf die Grafik passieren soll und was bei Click auf den Text (<click>, <txtclick>) und eine Progressbar kann es geben, mehr dazu beim Genmon-Link unten. Ich könnte noch die aktuelle Zahl vom Blogcounter abholen, für zwischen den vollen Stunden bei click hier, und bei click da die Grafik mit convert größer skalieren und mit display anzeigen.

Update 1: Eine vergrößerte Ansicht habe ich jetzt realisiert, und zwar mit ohne convert, das macht display  (auch im Paket ImageMagick, falls ich das noch nicht geschrieben habe) alleine, durch die Codezeile:

echo "<click>display -resize 480x320 $img.xpm</click>"

Das war ja einfach.

Und ein Hinweis noch: Zum Synchronisieren des Plugins mit der Abfrage der Werte durch cron, bei mir immer zur vollen Stunde, kann man in der Kommandozeile ein at-Kommando absetzen (at, wie das englische at in at 2pm oder at teatime), gibt man, z.B. für die 17:00 Uhr-Abfrage, das Kommando:

at 17:02
xfce4-panel --plugin-event=genmon-X:refresh:bool:true
(Ctrl-D)

ein. Statt X müsst Ihr einen Wert eingeben; wo der herkommt steht auf der genmon-Seite die unten verlinkt ist.

(end of update)

Aber irgendwo ist auch noch der Wurm drinnen. Der Maximalwert stimmt, aber das Log stimmt nicht mit den Werten überein, die aber unverdächtig wirken. Das werde ich noch suchen, wenn es nicht vorher ein Leser findet und im Kommentar erklärt. Sonst Updates hier, eher kein neuer Beitrag.  Schreibt einen Kommentar, wenn Ihr drauf wartet. 

Update 2: Dem column-Programm muss man den Schalter -x übergeben, sonst formatiert es die Zahlen 1 2 3 4 5, jeweils in einer Zeile übergeben, so:

1 4
2 5
3

statt so:

1 2
3 4
5

(end of update)

Und noch 2 Worte zu bekannten Schwächen: Wenn man den Rechner ausschaltet, dann legt er keine Messung an. Beim Anschalten haben sich dann vielleicht viele Visits angesammelt, aber auch über viele Stunden verteilt. Ich habe beschlossen die Zeit, obwohl sie mitprotokolliert wird, nicht zu berücksichtigen. Man könnte damit dann zwar einen breiten Block anlegen, und die Abrufe darauf verteilen, also nach 15 Stunden und 150 Aufrufen würde man einfach das Mittel nehmen, 10 pro Stunde. Das würde das Programm aber sehr aufblähen und kompliziert machen. Das Programm zeigt also in Wahrheit die letzten 48 Messpunkte an, nicht 48 Stunden.

Generell habe ich mir wenig Gedanken gemacht, was alles schief gehen kann, und wie man dann die Situation am besten rettet. Da das Programm noch ganz frisch ist, sollte sich das im Laufe der Zeit von selbst ergeben.

Und bedanken muss ich mich bei den Entwicklern von GenMon. Das ist ja ein superflexibles Tool und einfach zu verwenden. Die Doku könnte noch ausführlicher sein, aber der erste Eindruck ist Klasse!

Hilfreiche Links:

Nachtrag:

Jetzt, wo ich meine eigene Lösung fertig habe, finde ich den neuen Ort, an dem WordPress die Ministatistik versteckt hat!

Und zwar wenn man den Blog in der Leseansicht offen hat, nicht im Bearbeitenmodus ist, und dann links oben „Meine Webseite“ klickt, dann klappt das Seitenmenü auf, und oben steht „Statistik“, daneben ein Balkendiagramm. Das ist nicht nur eine Erinnerungsgrafik für die, die vergessen haben, wie eine Statistik aussieht, sondern das sind die aktuellen Werte d. letzten 48 Stunden. Um das zu erkennen muss man freilich die aktuellen Werte kennen – gut, das war früher auch so. Auch dass es als Schaltfläche diente, um zur Statistikseite zu kommen. Ein Maximalwert zur Orientierung fehlt jetzt, aber wenn man die Zahl des letzten Tages kennt, kann man es wohl ungefähr abschätzen. Meine Lösung hat den Vorteil die Werte anzubieten, und immer sichtbar zu sein, aber den Nachteil Platz im Menü zu fressen. Ein anderer Vorteil für mich: Ich weiß jetzt wie es geht, wenn ich nochmal etwas derartiges brauche.

wp-statistik

Wie rechts nochmal zu sehen ist, mit anderer Proportion, sind das die gleichen Daten. Das hätte einem aber auch wer sagen können!

Und hier noch der Code als kompletter Block, ohne Gewähr:

#!/bin/bash
#
# (C) GPLv3, user unknown
#
# Erweiterung wird unten wechselnd angehängt (xpm/png) 
img=/home/stefan/.blogchart
# Bei mehr oder weniger als 48 Stunden hier anpassen (aber auch in der Funktion asciiart):
views=($(tail -n 48 ~/.bloglog | awk '{print $2}' | column -x))
# Debug:
# views=(5 4 10 8 6 6 5 4 4 3 5 0)
#
max () {
 maximum=0
 for view 
 do
   test $view -gt $maximum && maximum=$view
 done
 echo $maximum 
}

m=$(max ${views[@]})
# Avoid Div/0:
test $m -eq 0 && m=1

auf32 () {
 m=$1
 shift
 # echo Parameter: $#
 # Bei anderer Imagehöhe als 32 hier anpassen: 
 for hour
 do
   echo -n $((hour*32/m))" "
 done
 echo 
}

normalized=$(auf32 $m ${views[*]})

asciiart () {
 echo -e '/* XPM */'
 echo -e 'static char *XPM_example[] = {'
 # Erste Zahl ist Breite, zweite ist Höhe, also 48 32 für 2 Tage bei 32 Pixeln, sonst hier anpassen
 echo -e '"48 32 2 2 XPMEXT",'
 echo -e '"  m #ffffff",'
 echo -e '"* m #000000",'
 # Und hier Bildhöhe anpassen, Pixelzahl
 for row in {32..1}
 do
   echo -n '"'
   for traffic in ${normalized[@]}
   do
     test $traffic -ge $row && echo -ne "* " || echo -ne "  " 
   done
   echo '",'
 done
 echo '"XPMEXT author demystifikation.worpress.com GPLv3",'
 echo '"XPMENDEXT"'
 echo '};'
}

# echo ${views[@]}
# echo $m
# echo ${normalized[*]}

asciiart > $img.xpm
# convert $img.xpm $img.png 
# n=${#views[*]}
# echo "<txt>Max: $m Last: ${views[n-1]}</txt>"
# übler hack, hier auf row aus der Schleife der Funktion zuzugreifen. 
echo "<txt>Max: $m Last: $row</txt>"
# echo "<img>$img.png</img>"
echo "<img>$img.xpm</img>"
echo "<tool>views: ${views[@]} normalized: ${normalized[@]} </tool>"
echo "<click>display -resize 480x320 $img.xpm</click>"

 – WordPress Ministatistik, DIY – 

wp-mini-statistik

Vor 2-3 Tagen hat WordPress die Ministatistik, wie ich sie in etwa oben nachgemalt habe – einen Screenshot habe ich nicht – aus der Ansicht geworfen. Wer selbst keinen Blog hat, kennt das gar nicht – auch ich bekomme die nur auf meinem eigenen Blog angezeigt, nicht auf dem anderer Leute. Das war ein Minibalkendiagramm, das stündlich aktualisiert wurde, wenn man die Seite neu lud, und die letzten 48 h umfasste, erinnerte, je nach Besucherzahlen, manchmal an das Ciscologo, welches seinerseits auch an eine Hängebrücke erinnert.

Wer einen Premiumaccount hat, kann da vielleicht auch eigene Widgets platzieren. In dem von mir verwendeten 0-8-15-Layout im Free-Tarif war das allerdings keine Option die man aus-, abwählen oder platzieren konnte.

Ich habe es gleich schmerzlich vermisst. Bei den Einstellungsoptionen fand ich keinen passenden Ersatz. Unter Design/Widgets/Blogstatistik fand ich lediglich einen Zähler aller Besucher, der, wenn man die Überschrift und die Einheiten löscht, so aussieht:

zaehler

Den sehen jetzt alle, was mich nicht stört, außer der Platzverbrauch, aber man sieht natürlich nicht mehr wie viele pro Stunde da waren und muss die Zahl vom letzten Mal im Kopf haben, um daraus den jüngsten Verkehr abzuleiten. Nichts, womit ich mich belasten will, aber eine Basis um selbst was zu bauen.

Wer sein eigenes WordPressblog selbst hostet hat andere Möglichkeiten, aber da ich mich nicht um die Internas des Blogs kümmern will, absichern, Backups und das alles, muss ich mir anders behelfen.

Eine Webseite kann man auch programmgesteuert abrufen. Gut – das machen ja auch Firefox, Chromium, Safari und Opera, aber diese stellen sie auch grafisch dar. Man kann einfach den HTML-Code runterladen, und in diesem sieht der Zähler so aus (Zeilenumbrüche und Einrückung im Original anders):

<div id=“secondary“ class=“widget-area“ role=“complementary“>
<aside id=“blog-stats-4″ class=“widget widget_blog-stats“>

<h3 class=“widget-title“></h3>
<ul>
<li>184,914 </li>
</ul>
</aside>

Anhand der Zahl ist der Block leicht zu finden. Den Quellcode kann man sich auch im Browser ansehen, aber man will ja nicht stündlich die eigene Webseite manuell speichern, also das erledigt man mit wget:

wget https://demystifikation.wordpress.com/

für andere Webseiten eben mit einer anderen Adresse. Weil ich den Widget-Titel im Design gelöscht habe ist das leer bei mir. Gespeichert wird das runtergeladene unter index.html. Allerdings brauche ich gar nicht die ganze Datei sondern will da nur einen Teil aausfiltern, deswegen sage ich mit -O wo der Output hinsoll, und mit einem verlorenen Minuszeichen, dass es auf die Konsole ausgegeben werden soll.

wget -q https://demystifikation.wordpress.com/ -O -

Das -q steht für quiet und unterdrückt irgendwelche Statusmeldungen von wget, wieviel Prozent der Arbeit schon geleistet ist, und solchen Firlefanz.

Den Output filtere ich dann mit grep auf die Zeile, die den HTML-Code ‚<aside id=“blog-stats-4‘ enthält, wobei -A1 bewirkt, dass eine zusätzliche Zeile ausgegeben wird, und als zweiten Filter nutze ich sed, dem ich sage, dass ich vom Listitem nur die Folge an Ziffern und Kommazeichen will, und das Kommazeichen soll dann auch weg.

sed -nr "s/.*<li>([0-9,]+).<\/li>.*/\1/;s/,//p"

Wer oft HTML, XHTML und XML-Code parsen muss wird vielleicht lieber zu einem Tool wie xmlstarlet greifen, welches den Vorteil hat, robust gegenüber Zeilenumbrüchen zu sein oder dem Vorkommen von Suchbegriffen in Kommentaren, aber das ist hier nicht zu erwarten. Sollte sich das HTML ändern, dann womöglich auch gleich soviel, dass einem das auchnicht weiterhilft.

Im ganzen sieht die Kette dann so aus:

counter=$(wget -q https://demystifikation.wordpress.com/ -O - | grep -A1 '<aside id="blog-stats-4' | sed -nr "s/.*<li>([0-9,]+).<\/li>.*/\1/;s/,//p")

Jetzt steht in der Variablen counter nur noch die Zahl 184914 und gespeichert wurde gar nichts.

lastcount=$(< ~/.blogcounter)
increase=$((counter-lastcount))
#
old=$(date -r ~/.blogcounter +%s)
now=$(date +%s) 
timespan=$((now-old))
#
heat=$(test $timespan -ge 0 && echo $((increase*3600/timespan)) || echo "--")

Dann sage ich

  • lastcount soll der Inhalt der Datei ~/.blogcounter sein, eine versteckte Datei, die das Programm später selbst schreiben wird, so dass es beim ersten Mal einen Fehler meldet, weil die Datei nicht vorhanden ist.
  • increase ist einfach die Differenz von altem Counter und Istzustand
  • old ist der Zeitstempel der Datei Blogcounter in Sekunden seit dem 1.1.1970
  • timespan ist die Zeitdifferenz zu now, also jetzt

 

heat=$(test $timespan -gt 0 && echo $((increase*3600/timespan)) || echo "--")

Heat sollen die mittleren Besucher pro Stunde sein seit der letzten Protokollierung, also wenn nach 30 Minuten 10 gezählt wurden 20 und wenn die Messung ausgefallen ist (Rechner aus) eventuell 5, wenn es nach 2 h 10 waren.

Getestet wird, ob die Zeitspanne > (greater than) 0 ist, dann sei heat der Wert der Berechnung, sonst „–“ (Division durch Null vermeiden!).

Jetzt wird es ein wenig kompliziert – wieso?

Nun, einmal will ich das Programm nutzen, um nur die stündlichen Differenzen zu protokollieren. Andererseits will ich jederzeit abfragen können ohne etwas zu protokollieren, etwa weil ein Beitrag von einem Alphablogger verlinkt wird, und plötzlich 1000e Besuche bekommt, so dass ich im 10-Minutentakt nachsehe, was das Volk macht.

test 1$# -gt 10 && test $1 = "-w" && echo $counter > ~/.blogcounter \
|| echo -e "Anzahl Anstieg /h Intervall (min) Messpunkt\n-------------------------------------------------------"

Das test 1$# ist ein furchtbarer Hack. $# ermittelt, wieviele Argumente einem Skript übergeben wurden. Aus unerfindlichen Gründen ist das aber nicht 0, wenn es keine Argumente sind, sondern nix, leer, nada, Nil, void, „“. Und dann scheitert der Test. Also klebe ich eine 1 davor und tesst nicht auf größer als 0, sondern auf größer als 10. Eigentlich müsste es da was eleganteres geben.

Waren es mehr als 0 Argumente prüfe ich, ob der Inhalt des 1. Arguments „-w“ war (w wie write, schreiben). Mit mehr oder anderen Argumenten rechne ich übrigens nicht.

War auch dies prima, dann, und nur dann schreibe ich die aktuelle Zahl in die versteckte Datei, die oben bereits erwähnt wurde. Ansonsten gebe ich eine Überschrift aus.

echo -e "$counter $increase $heat $((timespan/60)) $(date '+%H:%M %F')"

So oder so gebe ich dann die Werte aus, Zählerstand, Differenz, Seitenaufrufe /h seit letzter Messung und das Datum. So sieht ein isolierter Scriptaufruf (blogcounter.sh) aus:

Anzahl Anstieg /h Intervall (min) Messpunkt
-------------------------------------------------------
185203 0        0        22       20:22 2017-01-08

Etwas müde, gerade?

Seit 20 Uhr 0 Besucher, das sind 0 pro Stunde, Messintervall sind 22 Minuten und gemessen heute um 20:22 Uhr.

Außerdem hat Linux ja einen cron-Daemon, der chronologisch Dienste verrichten kann, wenn man im sagt wann und was. Den stellt man so ein:

daemonen

hilfreiche Linuxdaemonen, im Hintergrund wirkend

 0 * * * * /home/stefan/bin/blogcounter.sh -w >> /home/stefan/.bloglog

Das heißt jeweils zur Minute 0, jede Stunde (*), jeden Tag der Woche, des Monats usw. soll das Skript oben, das bei mir unter /home/stefan/bin/blogcounter.sh liegt ausgeführt werden, und die Ausgabe an die Datei /home/stefan/.bloglog angehängt werden (die erstellt wird, wenn sie noch nicht existiert). Das -w unterdrückt die Ausgabe der Überschrift, die man nicht für jede Zeile im Log haben will, und sorgt dafür, dass der Wert auch in die Datei ~/.blogcounter geschrieben wird.

Und dann gibt es noch ein kleines Script bloglog.sh, das so aussieht:

#!/bin/bash
#
# Blogtrafficlog der letzten 48 Stunden ausgeben
#
echo -e "Anzahl Anstieg Heat(/h) Intervall (min) Messpunkt"
echo "-------------------------------------------------------"
tail -n 48 ~/.bloglog | tac

Tail gibt nur die letzten 48 Zeilen der Datei ~/.bloglog aus, tac ist das Gegenteil von cat und gibt die Zeilen in umgekehrter Reihenfolge aus, also die jüngsten zuerst. Je nach Bildschirm und Fontgröße empfiehlt es siich, die Zahl an die eigenen Bedürfnisse anzupassen.

Die Ausgabe von bloglog.sh sieht beispielsweise so aus:

Anzahl Anstieg Heat(/h) Intervall (min) Messpunkt
-------------------------------------------------------
185205  1  1 60 22:00 2017-01-08
185204  1  1 60 21:00 2017-01-08
185203 22 21 60 20:00 2017-01-08
185181  0  0 59 19:00 2017-01-08
185181  4  4 60 18:00 2017-01-08
185177 10 10 60 17:00 2017-01-08
185167  3  2 60 16:00 2017-01-08
185164  7  7 59 15:00 2017-01-08
185157  4  4 60 14:00 2017-01-08
185153  3  3 60 13:00 2017-01-08
185150 185150 187177 59 12:00 2017-01-08
 -185118 -183134 60 11:00 2017-01-08
185118 16 16 60 10:00 2017-01-08
185102  5  4 60 09:00 2017-01-08
185097  0  0 59 08:00 2017-01-08
185097  0  0 60 07:00 2017-01-08
...

Das genaue Layout kann mit WordPressmitteln nicht ohne übermäßigen Aufwand getreu wiedergegeben werden – ich denke so geht es.

Die zwei Zeilen, die hervortreten deuten darauf hin, dass der Counter um 11:00 nicht lesbar war, auf Null gesetzt wurde, so dass die Zahl einbrach um dann hochzuschnellen – ein Grund dafür ist mir nicht erkenntlich, aber es zeigt, dass das Skript insofern robust ist, als es 2 Stunden später wieder brav seinen Job tut.

Pro Stunde fallen ca. 25 Bytes Daten an, das macht 600 am Tag, rd. 220.000 im Jahr und in 100 Jahren 22 MB – dürfte also, selbst wenn es vergessen wird und ständig, über viele Systemaktualisierungen weiterläuft, nie Probleme verursachen.

Hier nochmal das ganze  blogcounter.sh-Skript am Stück:

#!/bin/bash 
#
# Blogcounter ermitteln
# Zahl erfassen
# Differenz zur letzten Messung berechnen
# Differenz ausgeben
# Zahl speichern
# Zeit der letzten Messung erfassen
# Anstieg ermitteln
# Hits pro Stunde ermitteln
#
counter=$(wget -q https://demystifikation.wordpress.com/ -O - | grep -A1 '<aside id="blog-stats-4' | sed -nr "s/.*<li>([0-9,]+).<\/li>.*/\1/;s/,//p")
lastcount=$(< ~/.blogcounter)
increase=$((counter-lastcount))
#
old=$(date -r ~/.blogcounter +%s)
now=$(date +%s) 
timespan=$((now-old))
#
heat=$(test $timespan -gt 0 && echo $((increase*3600/timespan)) || echo "--")
# only write to counter file if param -w was given
# allows for hourly logged values, triggered by cron (with -w) and
# intermediate views without logging 
test 1$# -gt 10 && test $1 = "-w" && echo $counter > ~/.blogcounter || echo -e "Anzahl Anstieg /h Intervall (min) Messpunkt\n-------------------------------------------------------"
#
# Ergebnisse loggen. Das ganze sollte per cron stündlich laufen. Mit e. Script
# die letzten 24 oder 48 Messungen anchauen.
#
# Crontabzeile: 
# 0 * * * * /home/stefan/bin/blogcounter.sh -w >> /home/stefan/.bloglog
#
echo -e "$counter $increase $heat $((timespan/60)) $(date '+%H:%M %F')"

Weiterführende Hilfe bekommt man zu den einzelnen Befehlen mit

  • man wget
  • man sed
  • man tail
  • man date
  • man bash
  • help test
  • man tac
  • man cron
  • man crontab

und zu allen Befehlen, außer test, (was ein Kommando der Bash ist) auch mit PROGRAMM –help.

Vor einiger Zeit habe ich ein kleines Skript, journal.sh, vorgestellt, um Messwerte automatisch zu datieren und mit einem Stichwort in einer Datei abzulegen.

Angeregt durch Erzählmirnix, die auch den Blog Fettlogik überwinden betreibt, wollte ich meine Werte auch in grafischer Form aufbereitet sehen, aber nicht mit einer schnuckeligen App, einerseits weil ich gar kein Smartphone warte, andererseits will ich mein Gewicht auch nicht in der Cloud speichern und nach jwd leaken. Solche Apps scheinen oft mitteilungsfreudiger zu sein, als dem Anwender lieb ist.

gewicht

Bevor ich aber zum HowTo komme noch ein Abstecher zu einer anderen Nützlichkeit. Die Shell kennt Funktionen, und solche Funktionen kann man in der Datei ~/.bashrc ablegen. Dann stehen sie in jedem neu geöffneten Shellfenster zur Verfügung. Da ich mein Journal für immer wieder die gleichen Stichworte nutze (kg, ezig, rad) habe ich mir Funktionen geschrieben, die prüfen, ob ich neben dem Stichwort weitere Eingaben übergebe – wenn nicht wird mir nur die Liste bisheriger Werte ausgegeben. Ansonsten wird zuerst der neue Wert eingegeben. Und statt journal.sh 82,7 kg einzugeben gebe ich nur kg 82,7 oder kg ein.

Das ist für faule Menschen sehr praktisch.

kg ()
{
test $# -ge 1 && journal.sh $1 „kg“;
grep “ kg“ ~/journal.txt
}

ezig ()
{
test -n $# && journal.sh „ezig“ $1;
grep „ezig“ ~/journal.txt
}

Für ezig sieht es etwas anders aus, da dort ein Eintrag typischerweise unformatierter ist, etwa ezig 19,80 2 Päckchen neue Coils oder ezig 9,99 1L Basisliquid.

test $# -ge 1 prüft, ob die Zahl der Argumente größer-gleich 1 ist (-ge: greater-equal).

test -n $# prüft, ob die Argumentliste leer ist.

Soweit der Exkurs. kg liefert mir also eine Liste von Gewichtsmessungen, und in eine Grafik verwandelt man sie mit gnuplot. Angezeigt werden kann sie mit display, aber jeder andere Bildbetracher tut es, der das Bildformat beherrscht, etwa eog, inkview usw.

kg | sed ’s/^.. //;s/ kg//;s/,/./;s/ /., /‘ > gewicht.lst
gnuplot gewicht.plt && display gewicht.png

kg liefert also eine Liste an Gewichtsmessungen mit Datum.

So 2016.08.14 75.7 kg
Do 2016.08.04 76,8 kg
Di 2016.07.19 77.8 kg
So 2016.07.03 78.8 kg
So 2016.06.19 79.6 kg
Fr 2016.06.17 80.8 kg

Mit Sed schneide ich vorne den Wochentag weg. Wie man sieht erfasse ich undiszipliniert die Werte mal im deutschen, kontinentalen Format mit Komma, dann wieder im computernahen mit Punkt. Das zweite löscht das “ kg“ weg. Diese Kommas wandelt das dritte Sed-Kommando in Punkte um. Als Feldtrenner erwartet Gnuplot ein Komma, das macht das vierte Kommando und die Ausgabe leite ich in eine Datei gewicht.lst um.

Sed war hier ja schon gelegentlich Thema. Es steht für Strream-EDitor und wird von mir vor allem für solche Serien Suche-und-Ersetze-Kommandos genutzt (substitute). Jedes Semikolon trennt einen Befehl vom nächsten, daher vier Befehle. s/vorher/nachher/; bedeutet also Substituiere vorher durch nachher. Diese Kommandos sind leichter zu schreiben als zu lesen.

Dann kommt gnuplot zum Zuge, ein wahnsinnig mächtiges Werkzeug um ohne Open-Office Grafiken zu erzeugen – im Sinne von mathematischen, wissenschaftlichen Grafiken. Hier das Programm gewicht.plt:

set title „Gewicht nach Zeit“
set xdata time
set style data lines
# set terminal dumb feed 120 45
# set terminal svg
# set output „gewicht.svg“
#
set term png
set output „gewicht.png“
#
set timefmt „%Y.%m.%d.“
set format x „%m\n“
set xlabel „Monat“
set ylabel „Gewicht“
# # set autoscale y
set xrange [„2016.01.01″:“2016.12.31“]
set yrange [60:100]
# set autoscale y
set datafile separator „,“
plot „./gewicht.lst“ using 1:2 t „diaet“ w lines

Viele Kommandos sind sicher selbsterklärend; set title, xlabel, ylab el, xrange, yrange– das versteht sich von selbst aber die Syntax muss man natürlich kennen.

Das auskommentierte autoscale malt was, das Minimum und Maximum als Grenze nimmt. Damit würde aber über die Zeit der Graph mal auf der Gewichts- und immer auf der Zeitachse verzerrt – ich hätte lieber gleichbleibende Proportionen und muss dann an Sylvester das Programm anpassen. Dass auf der X-Achse die Zeit aufgetragen wird muss gesondert gesagt werden: set xdata time, Befehle um eine SVG-Grafik zu machen habe ich auskommentiert. Ebenso die praktische Option eine Asciigrafik im Terminal selbst auszugeben set terminal dumb feed 120 45  – das wären 120 Spalten bei 45 Zeilen und sollte natürlich an das eigene Terminal angepasst sein.

Das ganze einzurichten dauert natürlich, insbesondere wenn man die Programme erst installieren und sich in die Befehle einfuchsen muss, aber hat man es einmal eingerichtet ist es wesentlich komfortabler, als erst OpenOffice zu öffnen und eine CSV-Datei zu importieren, da die Achsen zu beschriften und den Grafiktyp auszuwählen oder den Wertebereich anzupassen. Für jede Messung will man auch nicht OpenOffice öffnen – dafür ist ein Kommando kg 77.7 doch sehr viel komfortabler.

Wer sich wundert, dass es im Graphen nur abwärts geht: Ich messe zwar mehrmals täglich, aber wegen der Wasserschwankung im Körper gibt es ein ständiges Auf-und-Ab. Also protokolliere ich nur den jeweils neuesten Rekord nach unten. Sollte ich wieder schwer zulegen würde ich wohl auch wieder einen höheren Wert protokollieren, aber zur Zeit läuft die Gesundschrumpfung ganz gut.

Sicher könnte man die Grafik noch ausbauen mit Linien für die Schwellwerte des BMI bei Übergewicht für die eigene Körpergröße, Adipositas I und II sowie Untergewicht, aber so bin ich erstmal zufrieden.

Display ist übrigens als Programm im Grafikpaket Image-Magick enthalten.

gnu

Die vorgestellten Skripte dürfen natürlich frei benutzt und nach Lust und Laune verändert werden.

 – OpenTechSummit, Berlin, 2016, Kalkscheune – 

kalkscheune-9105

Die Alkscheune, pardon, Kalkscheune, Nähe Bahnhof Friedrichstraße, ist, vor allem wegen des großartigen Innenhofs, geschickt und malerisch mit weißen Segeln verschattet, ein sehr netter Veranstaltungsort. kalkscheune-9098

Der doppelte Espresso mit 3,50 bezahlbar. Im polnischen Imbisszelt gab es  2 vegetarische Speisen und Bigos zu 4,50. Das sollte Sauerkraut, Weißkraut, Wurst und Fleisch enthalten. Allerdings enthielt die Portion, die an sich nicht klein war, quasi nur ein Stückelchen Fleisch, kleine Gulaschgröße und ein Fitzelchen Wurst, die man ohne große Akrobatik, gemeinsam auf einen Teelöffel bekommen hätte. Das fand ich dann doch enttäuschend. kalkscheune-9097

Überraschend hoch war der Frauenanteil. Bei Scalameetups kommt es vor, dass von 80 Leuten 2 Frauen dabei sind. Hier waren es bestimmt 30% Frauenanteil, sowohl im Publikum, als auch bei den Rednern, Ständen und Veranstaltern.

kalkscheune-9095

Ich vermute es lag daran, dass es einige Angebote für Kinder gab, von denen jetzt nicht alles voll war, aber 2 Handvoll werden es gewesen sein. Die Veranstalter, darauf angesprochen, fanden es nicht so außergewöhnlich, trugen zur Erklärung bei, dass es sich auch bei vielen Themen um Pädagogik, Lernen, Schule handele, in dem eben einige Frauen beschäftigt seien.

Hashtags: #OTS, #OpenTechSummit, #opntec