[vz-users] Probleme mit Zeilenumbruch in einem Import-Script

Lars Täuber lars.taeuber at web.de
Thu Dec 29 23:15:05 CET 2016


Hallo Dennis,

ich bin beim Formulieren von Antworten etwas ungeschickt. Bitte fasse die Mail nicht als Belehrung auf.
Ich schreibe mal, ein paar Grundbemerkungen auf und dann eine Lösung, wie ich es machen würde.

* Konvention in Shellskripten: Endung auf .sh; lassen sich so besser von AWK-Skripten (*.awk) usw. unterscheiden

* Konvention in Shellskripten: Variablennamen in Großbuchstaben: "${DATEIEN[$I]}"

* Wenn man Dateien in Shellskripten als Option übergibt, sollte man darauf achten, dass evtl Sonderzeichen (z.B. Leerzeichen) im Dateinamen stecken können. Das kann im einfachsten Fall zu unerwünschten Ergebnissen erführen, im schlimmsten Fall aber auch zu gefährlichen Sicherheitslücken. Also Dateinamen _immer_ in Anführungszeichen: "${dateien[$i]}".
  Wenn man sich daran hält auch wenn es eigentlich nicht notwendig ist, da man die Dateinamen selbst wählt, vergisst man es aber auch in anderen Fällen nicht so einfach.

* überflüssige Programmaufrufe möglichst vermeiden:
*** cat "$DATEI" | sed -n ...
besser:
*** sed -n ... "$DATEI"

* Die Ausgabe durch 'sed 's/.../.../p' bewirken, dass das Ergebnis an die Shell übergeben wird. Die Interpretiert das ";" nach dem "ts=" als Trenner zwischen Befehlen. Du willst aber folgendes der Shell übergeben:
vzclient -u 5d485110-9908-11e6-8e35-ade5ad34c96a add data value=XXXXXX ts=$(date -d "2013-05-28 09:20:00" +" %s000")

Das $(befehl) bedeutet, dass die Shell den Befehl ausführen soll und die Ausgabe als String einfügen soll. Daraus entsteht also:
vzclient -u 5d485110-9908-11e6-8e35-ade5ad34c96a add data value=XXXXXX ts=1369725600000

Und das wird dann interpretiert.


* die verschiedenen Ausdrücke "[0-9]*.[0-9]*;" beim sed sind etwas zu umständlich. Kürzer geht es so: alle Zeichen nur kein ";":
[^;]*;

* Die Klammern um den letzten Ausdruck "(.*)" sind nicht notwendig, weil Du den Inhalt nicht auswerten möchtest.


* Der Umweg über das Shell-Array ist nicht notwendig, wenn die Dateien alle in einem Verzeichnis liegen. Auch möglich ist hier:
for DATEI in *
do
	sed -n ... "$DATEI"
done


Ich bin mir nicht ganz sicher, was Du als Ergebnis erwartest, aber ich vermute in der ersten sed-Zeile soll der vierte Wert nach einem ";" als "value" genommen werden.

Man kann gut mit "cut" bestimmte Felder einer Datei extrahieren:
cut -d';' -f 2,4
oder kürzer, wenn man das ";" escaped:
cut -d\; -f 2,4
Das ";" wird sonst von der Shell interpretiert und nicht vom "cut"

Dann möchtest Du aber nur die Zeilen auswerten, die mit "600;" beginnen. Das kann man mit "sed" oder mit "grep" machen:
sed anweisen, dass es Zeilen, die mit "600;" anfangen nicht wegschmeißen soll:
sed -e '/^600\;/!d' -e 's/....'  "$DATEI"

oder:
grep '^600;' | ...

Oder man nimmt die Datumsangabe als Kriterium.

Ein '\' am Ende einer Zeile zeigt der Shell, dass der Befehl auf der nächsten Zeile weiter geht.
In der Shellvariable "IFS" steht, welche Zeichen als Trennzeichen interpretiert werden sollen.
Normalerweise stehen das Leerzeichen, Tabulator und "Newline" drin. Aber man kann das auch selbst beeinflussen:
IFS=';'
trennt nun auch "2013-05-28 09:20:00;264802N203;1159" in 3 Teile:
2013-05-28 09:20:00
264802N203
1159


Also ich stell mir das so vor:

#!/bin/bash
#
# importiere Werte aus Logdateien
#

IFS=';'

for DATEI in id*
do
  grep ';201.-..-.. ' $DATEI \
  | cut -d\; -f2,4,8,9,10 \
  | while read DATUM P_AC WERT5 WERT6 WERT7
    do
      DATUM=$(date -d "$DATUM" +%s000)
      vzclient -u 5d485110-9908-11e6-8e35-ade5ad34c96a add data value=$P_AC ts=$DATUM
      vzclient -u 681155e0-9906-11e6-b5d9-51edf01eb751 add data value=$WERT5 ts=$DATUM
      vzclient -u 6fc360c0-9906-11e6-bc21-7becbbfe157b add data value=$WERT6 ts=$DATUM
      vzclient -u 7664b9b0-9906-11e6-a73b-09873e88f0c0 add data value=$WERT7 ts=$DATUM
    done
done

Den Rest der Stellen war ich zu faul auszuzählen.
Jede Datei wird hier nur einmal mit grep, cut bearbeitet und jede Zeile nur einmal und dem read gelesen und auch nur ein "date" pro Zeile ausgeführt.
In Deiner Version wurde jede Datei für jeden Wert mehrfach geöffnet, bearbeitet und jedes mal das Datum neu berechnet.

Ich hoffe, das hilft Dir weiter.


Berthold Bredenkamp hat im Herbst 2011 ein Skript gebastelt:
http://volkszaehler.org/pipermail/volkszaehler-dev/attachments/20111127/69cb6074/attachment.obj
welches ich für meine Importe angepasst habe.
Dort wird so eine Zeile wie die 6. Zeile in deiner Datei vom Skript auswertet.
Ich gebe dann nur noch an, welche VZ-ID für welche Werte benutzt werden sollen, also 
P_AC für 5d485110-9908-11e6-8e35-ade5ad34c96a

Diese Verknüpfung lege ich in Variablen ab:
UUID["P_AC"]="5d485110-9908-11e6-8e35-ade5ad34c96a"

und das Skript findet dann selbständig heraus, an welcher Stelle der entsprechende Wert zu suchen ist.


Aber solange sich die Position der Werte in den Dateien nicht ändert, funktioniert das obere Skript natürlich schneller und ist auch viel besser lesbar.


Viel Erfolg und guten Rutsch.
Lars







PS: Bash ist ein kleines Hobby von mir. :-)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: import.sh
Type: text/x-sh
Size: 555 bytes
Desc: not available
URL: <http://demo.volkszaehler.org/pipermail/volkszaehler-users/attachments/20161229/5ce22766/attachment.sh>


More information about the volkszaehler-users mailing list