[BASH] Spalte in der letzten Zeile ersetzen?

brenner

Commander
Registriert
Apr. 2002
Beiträge
3.037
Irgendwie war ich fast fertig, aber nun komme ich nicht weiter. Ich möchte ein Logfile auswerten. Wenn sich der "CUM" Wert zwischen den letzten beiden Zeilen um mehr als 0.545 ändert, dann soll der CUM Wert der letzten Zeile mit dem CUM Wert der vorletzten Zeile überschrieben werden.


Das Logfile sieht im Original so aus:
Code:
2013-04-22_22:11:43 CUL_EM_6 CNT: 144 CUM: 97.209 5MIN: 0.000 TOP: 0.000
2013-04-22_22:12:43 CUL_EM_6 CNT: 144 CUM: 97.309 5MIN: 0.000 TOP: 0.000
2013-04-22_22:13:43 CUL_EM_6 CNT: 144 CUM: 98.309 5MIN: 0.000 TOP: 0.000



Errechnet habe ich die Differenz folgendermaßen:

Code:
#CUM Wert der letzten Zeile
SUM_COR_VAR1=`tail -n1 "$LOG_FILE_CNT" | awk '{print $6}' | sed 's/\.//'`; 

#CUM Wert der vorletzten Zeile
SUM_COR_VAR2=`tail -n2 "$LOG_FILE_CNT" | head -n1 | awk '{print $6}' | sed 's/\.//'`; 

#Differenz zwischen beiden Werten
SUM_COR_VAR3=$(($SUM_COR_VAR1 - $SUM_COR_VAR2))



Die Schleife sieht so aus (Script wird von aussen aufgerufen im Minutentakt):

Code:
if [[ "$SUM_COR_VAR3" -ge 545 ]]; then
     #Hier soll das Ersetzen des CUM Wertes rein
fi


Und nun ist meine Hürde/n da, ich muss jetzt quasi "NUR" noch die entsprechende Spalte der letzten Zeile gegen die der vorletzten austauschen :(
 
Schau dir "sed" und "awk" an. Damit kannst du das einfach ersetzen.

Das war natürlich dumm von mir, hast die Befehle ja schon benutzt. Bitte meinen Eintrag ignorieren ;-)
 
Zuletzt bearbeitet:
Sollte doch so funktionieren:

Code:
    #CUM Wert der letzten Zeile
    SUM_COR_VAR1=`tail -n1 "$LOG_FILE_CNT" | awk '{print $6}' | sed 's/\.//'`;

   # komplette letzte Zeile
   SUM_COR_LAST=`tail -n1 "$LOG_FILE_CNT"`;

     
    #CUM Wert der vorletzten Zeile
    SUM_COR_VAR2=`tail -n2 "$LOG_FILE_CNT" | head -n1 | awk '{print $6}' | sed 's/\.//'`;
     
    #Differenz zwischen beiden Werten
    SUM_COR_VAR3=$(($SUM_COR_VAR1 - $SUM_COR_VAR2))
 

    if [[ "$SUM_COR_VAR3" -ge 545 ]]; then
     t=$(echo  $SUM_COR_LAST | sed 's|$SUM_COR_VAR1|$SUM_COR_VAR2|g')
    fi

Oder du Baust dir die Zeile komplett mit awk.
 
Ich bin jetzt spontan nicht mit der t=$(...) Syntax vertraut, vermute aber, dass danach die geänderte Zeile nur in der Variable t steht. Dadurch ist die Datei jedoch noch nicht geändert. Falls ich dir damit Unrecht tue, entschuldige ich mich hiermit und würde mich über eine Erklärung freuen.

Ich würde es so machen:
Code:
#CUM Wert der letzten Zeile
SUM_COR_VAR1=`tail -n1 "$LOG_FILE_CNT" | awk '{print $6}' | sed 's/\.//'`;
 
# komplette letzte Zeile
SUM_COR_LAST=`tail -n1 "$LOG_FILE_CNT"`;
 
     
#CUM Wert der vorletzten Zeile
SUM_COR_VAR2=`tail -n2 "$LOG_FILE_CNT" | head -n1 | awk '{print $6}' | sed 's/\.//'`;
     
#Differenz zwischen beiden Werten
SUM_COR_VAR3=$(($SUM_COR_VAR1 - $SUM_COR_VAR2))
 
 
if [[ "$SUM_COR_VAR3" -ge 545 ]]; then
    # Schreibt die Originaldatei bis auf die letzte Zeile in eine neue temporäre Datei
    head -n -1 $LOG_FILE_CNT > $LOG_FILE_CNT.tmp
    # Schreibt die letzte nun geänderte Zeile in die temporäre Datei
    echo "$SUM_COR_VAR2 $SUM_COR_LAST" | awk '{print $2" "$3" "$4" "$5" "$6" "$1" "$8" "$9" "$10" "$11}' >> $LOG_FILE_CNT.tmp
    # Überschreibt die Originaldatei mit der temporären.
    mv $LOG_FILE_CNT.tmp $LOG_FILE_CNT
fi

Das awk Kommando sieht irgendwie nicht schön aus, aber es ist spät und mir ist spontan nichts besseres eingefallen.
 
ThrillPanda schrieb:
Sollte doch so funktionieren:

Code:
    #CUM Wert der letzten Zeile
    SUM_COR_VAR1=`tail -n1 "$LOG_FILE_CNT" | awk '{print $6}' | sed 's/\.//'`;

   # komplette letzte Zeile
   SUM_COR_LAST=`tail -n1 "$LOG_FILE_CNT"`;

     
    #CUM Wert der vorletzten Zeile
    SUM_COR_VAR2=`tail -n2 "$LOG_FILE_CNT" | head -n1 | awk '{print $6}' | sed 's/\.//'`;
     
    #Differenz zwischen beiden Werten
    SUM_COR_VAR3=$(($SUM_COR_VAR1 - $SUM_COR_VAR2))
 

    if [[ "$SUM_COR_VAR3" -ge 545 ]]; then
     t=$(echo  $SUM_COR_LAST | sed 's|$SUM_COR_VAR1|$SUM_COR_VAR2|g')
    fi

Oder du Baust dir die Zeile komplett mit awk.



Klappt leider nicht. Zu einem schneide ich ja bei VAR1 und VAR2 den Punkt raus um damit rechnen zu können. Dann "deine" sed unten natürlich die Zahl nicht mehr finden und ersetzen da 12.34 nicht gleich 1234 ist.
Aber auch mit Punkt drin funktiert das nicht, aber ich habe zumindestens verstand was du wolltest.
Ergänzung ()

Freezedevil schrieb:
Ich bin jetzt spontan nicht mit der t=$(...) Syntax vertraut, vermute aber, dass danach die geänderte Zeile nur in der Variable t steht. Dadurch ist die Datei jedoch noch nicht geändert. Falls ich dir damit Unrecht tue, entschuldige ich mich hiermit und würde mich über eine Erklärung freuen.

Ich würde es so machen:
Code:
#CUM Wert der letzten Zeile
SUM_COR_VAR1=`tail -n1 "$LOG_FILE_CNT" | awk '{print $6}' | sed 's/\.//'`;
 
# komplette letzte Zeile
SUM_COR_LAST=`tail -n1 "$LOG_FILE_CNT"`;
 
     
#CUM Wert der vorletzten Zeile
SUM_COR_VAR2=`tail -n2 "$LOG_FILE_CNT" | head -n1 | awk '{print $6}' | sed 's/\.//'`;
     
#Differenz zwischen beiden Werten
SUM_COR_VAR3=$(($SUM_COR_VAR1 - $SUM_COR_VAR2))
 
 
if [[ "$SUM_COR_VAR3" -ge 545 ]]; then
    # Schreibt die Originaldatei bis auf die letzte Zeile in eine neue temporäre Datei
    head -n -1 $LOG_FILE_CNT > $LOG_FILE_CNT.tmp
    # Schreibt die letzte nun geänderte Zeile in die temporäre Datei
    echo "$SUM_COR_VAR2 $SUM_COR_LAST" | awk '{print $2" "$3" "$4" "$5" "$6" "$1" "$8" "$9" "$10" "$11}' >> $LOG_FILE_CNT.tmp
    # Überschreibt die Originaldatei mit der temporären.
    mv $LOG_FILE_CNT.tmp $LOG_FILE_CNT
fi

Das awk Kommando sieht irgendwie nicht schön aus, aber es ist spät und mir ist spontan nichts besseres eingefallen.


Mit dem Umweg über eine seperate Datei habe ich auch schon Mal nachgedacht, aber es hat mir innerlich widerstrebt das so umständlich zu machen ;-)
Wenn ich es richtig sehe, wird die alte Logdatei (also deren kompletter Inhalt, mit einem Einzeiler überschrieben, oder?


Ich werde mal versuchen aus euren beiden Ideen etwas hinzufrickeln.
Ergänzung ()

Juhu, ich habs, nach einigem hin und her testen.

Danke für die Lösungsansätze!



Hier das funktionsfähige Ergebnis:

Code:
###############################################################################
# Fehlmessung des EM1000-HSM bei rueckwaerts fliessenden Stroemen ausgleichen #
###############################################################################

testfile="/home/pi/LOGFILE-test.log"

#CUM Wert rauscutten und Punkt zum Differenzberechnen entfernen
SUM_COR_VAR1=`tail -n1 "$testfile" | awk '{print $6}' | sed 's/\.//'`;
SUM_COR_VAR2=`tail -n2 "$testfile" | head -n1 | awk '{print $6}' | sed 's/\.//'`; 
#Differenz berechnen
SUM_COR_VAR3=$(($SUM_COR_VAR1 - $SUM_COR_VAR2))

#Wenn die Differenz zu hoch (also falsch) ist, wird der CUM Wert der letzten Zeile durch den der vorletzten Zeile ersetzt
if [[ "$SUM_COR_VAR3" -ge 545 ]]; then
	#Ausgeschnittene CUM Werte zum ersetzen mit Punkt
	SUM_COR_VAR11=`tail -n1 "$testfile" | awk '{print $6}'`
	SUM_COR_VAR22=`tail -n2 "$testfile" | head -n1 | awk '{print $6}'`
	#Pendant zu LZ=`tail -n1 "$LOG_FILE_CNT"` zum testen
	SUM_COR_LAST=`tail -n1 "$testfile"`
 	#Letzte Zeile loeschen
	sed -i '$D' $testfile
	#Zeilenwert ersetzen und neue Zeile in Logdatei schreiben
	echo $(echo  $SUM_COR_LAST | sed "s/$SUM_COR_VAR11/$SUM_COR_VAR22/g") >> $testfile
fi

###############################################################################
 
$() macht nichts anderes als ein Backtick, es führt einfach den darin stehenden Befehl aus, sieht aber etwas netter aus als `` (mMn)

Was ich selbst eben erst durchs probieren rausgefunden habe ist, dass man wenn man innerhalb sed mit Variablen Arbeiten möchte muss man " statt ' verwenden muss.

Das einzige was noch irgendwie blöd ist das ersetzen der Zeile in der selben Datei irgendwie nicht so will wie ich, daher als Output zum testen mal /tmp/newfile.txt

Code:
#!/bin/bash

LOG_FILE_CNT="/tmp/test.log"

# CUM Wert der letzten Zeile
SUM_COR_VAR1=$(tail -n 1 "$LOG_FILE_CNT" | awk '{ print $6 }' | sed 's/\.//');
SUM_COR_VAR1_COMPLETE=$(tail -n 1 "$LOG_FILE_CNT" | awk '{ print $6 }')

# komplette letzte Zeile
SUM_COR_LAST=$(tail -n 1 "$LOG_FILE_CNT");

#CUM Wert der vorletzten Zeile
SUM_COR_VAR2=$(tail -n 2 "$LOG_FILE_CNT" | head -n 1 | awk '{ print $6 }' | sed 's/\.//');
SUM_COR_VAR2_COMPLETE=$(tail -n 2 "$LOG_FILE_CNT" | head -n 1 | awk '{ print $6 }')

#Differenz zwischen beiden Werten
SUM_COR_VAR3=$(($SUM_COR_VAR1 - $SUM_COR_VAR2))

if [ "$SUM_COR_VAR3" -ge "545" ]
 then
  SUM_COR_NEW_LINE=$(echo $SUM_COR_LAST | sed "s|$SUM_COR_VAR1_COMPLETE|$SUM_COR_VAR2_COMPLETE|g")
  sed -e "s|$(echo $SUM_COR_LAST | sed -e 's|\ |\\ |g' -e 's|\:|\\:|g' -e 's|\.|\\.|g')|$(echo $SUM_COR_NEW_LINE | sed -e 's|\ |\\ |g' -e 's|\:|\\:|g' -e 's|\.|\\.|g')|g" < $LOG_FILE_CNT > /tmp/newfile.out
 fi

Edit:
da habe ich wohl etwas zulange getippt. :/
 
Zuletzt bearbeitet:
Vielen Dank für die Erklärung Panda. Evtl. werde ich das in zukunft auch anstatt `` verwenden.

@ brenner: Die Lösung mit dem tempfile hat mich auch nicht so richtig zufrieden gestellt. Die Sache mit dem in-place edit von sed kannte ich nicht und so wie du es jetzt hast ist es auf jeden Fall eine recht elegante Lösung, die denke ich schwer zu toppen ist. Man könnte den Ablauf noch etwas optimieren, aber im Prinzip gewällt mir das ziemlich gut.
 
Zurück
Oben