Zahlenwerte und Namen mit shellskript auslesen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Zahlenwerte und Namen mit shellskript auslesen

Beitrag von smutbert » 19.02.2021 17:45:40

Hallo allerseits

es wird mir langsam peinlich, aber ich komme schon wieder nicht weiter. Dieses Mal bin ich aber nah dran. Ich möchte aus der Ausgabe von sensors, zB

Code: Alles auswählen

iwlwifi_1-virtual-0
Adapter: Virtual device
temp1:        +34.0°C  

pch_cannonlake-virtual-0
Adapter: Virtual device
temp1:        +38.0°C  

acpitz-acpi-0
Adapter: ACPI interface
temp1:        +16.8°C  (crit = +20.8°C)
temp2:        +27.8°C  (crit = +119.0°C)

coretemp-isa-0000
Adapter: ISA adapter
Package id 0:  +30.0°C  (high = +94.0°C, crit = +100.0°C)
Core 0:        +27.0°C  (high = +94.0°C, crit = +100.0°C)
Core 1:        +29.0°C  (high = +94.0°C, crit = +100.0°C)
Core 2:        +27.0°C  (high = +94.0°C, crit = +100.0°C)
Core 3:        +28.0°C  (high = +94.0°C, crit = +100.0°C)
Core 4:        +29.0°C  (high = +94.0°C, crit = +100.0°C)
Core 5:        +27.0°C  (high = +94.0°C, crit = +100.0°C)

nvme-pci-0300
Adapter: PCI adapter
Composite:    +34.9°C  (low  = -273.1°C, high = +80.8°C)
                       (crit = +80.8°C)
Sensor 1:     +34.9°C  (low  = -273.1°C, high = +65261.8°C)
Sensor 2:     +35.9°C  (low  = -273.1°C, high = +65261.8°C)
die Namen in den ersten Zeilen der Absätze (also iwlwifi_1-virtual-0, pch_cannonlake-virtual-0, acpitz-acpi-0,...) und die zugehörigen Temperaturwerte, ohne Grenzwerte auslesen.

Folgendes kleines Skript ist bei meinen Bemühungen herausgekommen

Code: Alles auswählen

#!/bin/sh

Name="unknown"
MaxTemp=0

sensors | while read line; do
        if echo "${line}" | grep -e '[^ ]-' > /dev/null; then
                Name="$(echo ${line} | grep -oe '^[a-z]*')"
        elif echo "${line}" | grep ' +' > /dev/null; then
                Temp=$(echo "${line}" | awk -F "+" '{ split($2, arr, "°C"); printf("%d", arr[1] + 0.5) }')
                if test ${Temp} -gt ${MaxTemp}; then
                        MaxTemp="${Temp}"
                        MaxName="${Name}"
                fi
        fi
done

echo "Maximum Temperature: $MaxName = $MaxTemp"
Es gibt aber zwei Probleme, bei denen ich nicht so recht weiter weiß:
  1. $MaxTemp und $MaxName gehen verloren. Ich vermute, dass die Schleife in einer Subshell ausgeführt wird, weiß aber nicht wie ich das vernünftig umgehen kann.
  2. ich habe es nicht geschafft die vorvorletzte Zeile herauszufiltern - die mit "(crit = +80.8°C)", das ich werde momentan die fixen 80.8°C als Maximaltemperatur nicht los.
(andere Verbesserungsvorschläge sind natürlich ebenfalls herzlich willkommen)

lg smutbert
Zuletzt geändert von smutbert am 20.02.2021 00:14:08, insgesamt 1-mal geändert.

eggy
Beiträge: 3334
Registriert: 10.05.2008 11:23:50

Re: Zahlenwerte und Namen mit shellskript auslesen

Beitrag von eggy » 19.02.2021 18:55:08

Code: Alles auswählen

 sensors -u 
Ich hab bei Deiner Beschreibung nicht verstanden, was zum Beispiel im Fall von coretemp-isa-0000 ausgegeben werden soll.

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: Zahlenwerte und Namen mit shellskript auslesen

Beitrag von smutbert » 19.02.2021 19:04:45

Oh, ich habe nach dem Schreiben meines Beitrags noch etwas geändert. Es sollte dann "coretemp" ausgegeben werden (alles bis zum ersten Zeichen, das kein Kleinbuchstabe ist), aber mit mit sensors -u sieht es ja gleich viel freundlicher aus.

eggy
Beiträge: 3334
Registriert: 10.05.2008 11:23:50

Re: Zahlenwerte und Namen mit shellskript auslesen

Beitrag von eggy » 19.02.2021 19:19:58

Sorry, ich versteh die Beschreibung noch nicht

Code: Alles auswählen

coretemp-isa-0000
Adapter: ISA adapter
Package id 0:  +30.0°C  (high = +94.0°C, crit = +100.0°C)
Core 0:        +27.0°C  (high = +94.0°C, crit = +100.0°C)
Core 1:        +29.0°C  (high = +94.0°C, crit = +100.0°C)
Core 2:        +27.0°C  (high = +94.0°C, crit = +100.0°C)
Core 3:        +28.0°C  (high = +94.0°C, crit = +100.0°C)
Core 4:        +29.0°C  (high = +94.0°C, crit = +100.0°C)
Core 5:        +27.0°C  (high = +94.0°C, crit = +100.0°C)
Was erwartest Du hier als Ausgabe?

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: Zahlenwerte und Namen mit shellskript auslesen

Beitrag von smutbert » 19.02.2021 20:54:36

So weit, dass ich etwas ausgeben will, bin ich ja eigentlich noch nicht, aber mein erstes Zwischenziel war (das würde ich mit »sensors -u« jetzt schaffen)

Code: Alles auswählen

iwlwifi 34
pch 38
acpitz 17
acpitz 28
coretemp 30
coretemp 27
coretemp 29
coretemp 27
coretemp 28
coretemp 29
coretemp 27
nvme 35
nvme 35
nvme 36
bzw. am Ende nur mehr die Ausgabe der höchsten aufgetretenen Temperatur (oder auch der höchsten 2 Temperaturen unterschiedlicher Geräte/Namen), da bin ich noch nicht weiter

Code: Alles auswählen

pch 38
nvme 36

Benutzeravatar
Meillo
Moderator
Beiträge: 9224
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Zahlenwerte und Namen mit shellskript auslesen

Beitrag von Meillo » 19.02.2021 21:53:40

Auch wenn es deine Wuensche vielleicht nicht genau trifft, ich hatte Lust darauf es zu programmieren. Hier der hoechste Wert je Namen:

Code: Alles auswählen

awk '{if ($2>a[$1]) {a[$1]=$2}} END{for (i in a) {print i, a[i]}}'
Hier eine alternative Implementierung:

Code: Alles auswählen

sort -k 1,1 -k 2nr | awk '{print $2,$1}' | uniq -f 1 | awk '{print $2,$1}'
(Uniq kennt zwar symmetrisch -s/-w aber bei -f fehlt das Gegenstueck, darum das doppelte Umdrehen mit awk.)


Wenn du davon nur die zwei hoechsten Werte haben willst, dann pipe anschliessend noch durch:

Code: Alles auswählen

sort -k 2nr | head -n 2

... vielleicht kannst du ja irgendwas davon brauchen, auch wenn's nicht auf dem direkten Weg zum Ziel liegt.
Use ed once in a while!

eggy
Beiträge: 3334
Registriert: 10.05.2008 11:23:50

Re: Zahlenwerte und Namen mit shellskript auslesen

Beitrag von eggy » 20.02.2021 03:23:25

smutbert hat geschrieben: ↑ zum Beitrag ↑
19.02.2021 20:54:36
So weit, dass ich etwas ausgeben will, bin ich ja eigentlich noch nicht
Ich find's oftmals einfacher "rückwärts" vorzugehen: Was will ich überhaupt am Ende haben? Wie komm ich dahin? Welche Struktur ist in den Daten vorhanden?

In dem Fall: Einzelne Blöcke, mit Leerzeilen (^=Zeilenanfang, $=Zeilenende, nichts dazwischen) getrennt.

Code: Alles auswählen

 awk '{ print $0}  /^$/{print "-----------------"; }' 
Als nächstes fällt auf: in der erste Zeile jedes Blockes haben wir den Namen.

Nächster Schritt nen Zähler einbauen, der bei nem neuen Block auf null gesetzt wird. Damit wir abzählen können wo die erste Zeile ist.

Code: Alles auswählen

awk 'BEGIN {ZAEHLER=0} { ZAEHLER++; print ZAEHLER " " $0}  /^$/{print "-----------------"; }' 
und bei nem neuen Block resetten:

Code: Alles auswählen

awk 'BEGIN {ZAEHLER=0} { ZAEHLER++; print ZAEHLER " " $0}  /^$/{print "-----------------"; ZAEHLER=0 }'  
nächster Schritt merken wir uns den Namen der in Zeile 1 steht:

Code: Alles auswählen

awk 'BEGIN {ZAEHLER=0} { ZAEHLER++; print ZAEHLER " " $0} ZAEHLER==1{NAME=$0; next} /^$/{print "-----------------"; ZAEHLER=0 }'  
next ist dazu da, awk zu sagen, dass es sich mit der Zeile nicht weiter beschäftigen soll

Dann schauen wir uns nochmal die Daten an, was ist in jeder Zeile, in der Werte enthalten sind, immer und nur da? Doppelpunkt ist dabei, aber den gibts auch bei den Adaptern; Klammern sind dabei, aber die gibts auch ohne Gerät; aber Doppeltpunkt und Kringel kommt genau da vor wo unsere Daten stehen.
Testen wir das mal

Code: Alles auswählen

awk '/:.*°/{print $0}' 
sieht schonmal ok aus.

Jetzt schneiden wir noch alles weg, was nicht gebraucht wird:
Den Teil hinten bekommt man ganz einfach weg:

Code: Alles auswählen

awk '/:.*°/{sub ("°.*",""); print $0}'  
Den Teil davor eigentlich auch, nur muss man bei dem Plus entsprechend quoten, oder einfach tricksen:

Code: Alles auswählen

 awk '/:.*°/{sub ("^[^+]*.",""); print $0}' 
An der Stelle "suche vom Zeilenanfang ("^" ) alles was kein Plus ("[^+]") ist und dann noch ein weiteres beliebiges Zeichen (".", was dann das Plus ist).
Kombiniert man die beiden:

Code: Alles auswählen

 awk '/:.*°/{sub ("^[^+]*.",""); sub ("°.*","");  print $0}'  
hat man die Werte in $0.
Zusammengebastelt siehts dann so aus:

Code: Alles auswählen

 awk 'BEGIN {ZAEHLER=0} { ZAEHLER++; print ZAEHLER " " $0} ZAEHLER==1{NAME=$0; next}    /^$/{print "-----------------"; ZAEHLER=0 }   /:.*°/{sub ("^[^+]*.",""); sub ("°.*","");  print $0} '  
Streichen wir mal alles raus, was wir nicht mehr brauchen:

Code: Alles auswählen

awk 'BEGIN {ZAEHLER=0} { ZAEHLER++; } ZAEHLER==1{NAME=$0; next}    /^$/{ ZAEHLER=0 }   /:.*°/{sub ("^[^+]*.",""); sub ("°.*","");  print NAME " " $0} ' 
Und jetzt kommt der Trick namens "named array", wir hatten vorhin ja die Namen für die Blöcke gefunden und in NAME gespeichert.
Die Werte legen wir jetzt in den Feldern des Arrays ab, und zwar immer nur dann, wenn der Wert größer als der bereits gespeicherte ist.

Code: Alles auswählen

awk 'BEGIN {ZAEHLER=0} { ZAEHLER++; } ZAEHLER==1{NAME=$0; next}    /^$/{ ZAEHLER=0 }   /:.*°/{sub ("^[^+]*.",""); sub ("°.*","");   if ($0>arr[NAME]){arr[NAME]=$0}} END { for (key in arr) { print key ": " arr[key] }}' 
Und am Ende des Programms (END{...}) geben wir noch das Array aus, nämlich den Feldnamen und den gespeicherten Wert.

Das Spiel mit named arrays macht zwar auf diversen awk Implementationen die Reihenfolge kaputt (genauer: sie ist nicht garantiert), ist in den meisten Fällen aber vermutlich egal, da man in solchen Fällen eh oft noch nen "| sort" anhängt.

Spoiler: anders geht's vermutlich eleganter. Aber vielleicht hilft Dir der gezeigte Ablauf Dich etwas weiter mit awk anzufreunden. Anfangs wirkt das ziemlich wirr. Aber wenn man die nötigen Strukturen gefunden hat, ist der Rest dann meist auch relativ einfach mit awk zu lösen.

Bei mir gibt sensors übrigens noch V Werte für die Batterie aus, das hab ich mal ignoriert. Und falls Du die Werte für irgendwas sinnvolles nehmen willst, denk dran, eine temp4 > 40 kann schlimmer sein als eine temp2 > 80. Wo die einzelnen Sensoren platziert sind, ist für die Interpretation der Werte wichtig. Und das kann sich von Gerät zu Gerät unterscheiden. Von daher ist "höchster Wert pro Gruppe" vielleicht nicht der sinnvollste Ansatz.

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: Zahlenwerte und Namen mit shellskript auslesen

Beitrag von smutbert » 20.02.2021 19:46:02

Danke, ihr seid großartig.
eggy hat geschrieben: ↑ zum Beitrag ↑
20.02.2021 03:23:25
Und falls Du die Werte für irgendwas sinnvolles nehmen willst, denk dran, eine temp4 > 40 kann schlimmer sein als eine temp2 > 80. [...]
Dessen bin ich mir natürlich bewußt, es geht mir aber hauptsächlich darum einen groben Überblick zu behalten – der Sinn beschränkt sich also darauf einige der Temperaturen in der sway-Statuszeile auszugeben.

Antworten