Nobodywolf
Anmeldungsdatum: 25. September 2013
Beiträge: 7
|

14. Mai 2022 10:51
(zuletzt bearbeitet: 14. Mai 2022 14:51)
Hallo
Bin gut beim kopieren und ausprobieren, immer und immer wieder, bis das Ergebnis stimmt.
Das Resultat ist oft ein langer und komplizierte Befehl den ich kaum verstehe.
Könnt ihr mir helfen diesen Befehl zu vereinfachen und den awk Teil erklären. | megacli -PDlist -aALL -NoLog | egrep 'Slot|state|Error Count|flagged' | awk '/Slot/{if (x)print x;x="";}{x=(!x)?$0:x " | "$0;}END{print x;}' | sed 's/ Number//g' | sed 's/ Count//g' | sed 's/Firmware state: //g' | sed 's/Drive has flagged a S.M.A.R.T/SMART/g' | column -t
|
Besten Dank. Bearbeitet von rklm: Codeblock. Bitte beachte Forum/Syntax und nutze die Vorschaufunktion!
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 405
|

14. Mai 2022 13:20
(zuletzt bearbeitet: 14. Mai 2022 13:33)
Hallo Nobodywolf, ich habe die Zeile mal etwas lesbarer in folgendem Block dargestellt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | megacli -PDlist -aALL -NoLog |
egrep 'Slot|state|Error Count|flagged' |
awk '
/Slot/ {
if (x) print x;
x="";
}
{
x=(!x)?$0:x " | " $0;
} END {
print x;
}' |
sed 's/ Number//g' |
sed 's/ Count//g' |
sed 's/Firmware state: //g' |
sed 's/Drive has flagged a S.M.A.R.T/SMART/g' |
column -t
|
In Zeile 1 wir das Programm "megacli" aufgerufen, dessen Ausgabe in Zeile 2 durch "egrep" nach verschiedenen Strings (Slot oder stat oder ...) durchsucht wird. die so gefundenen Zeilen werden ausgegeben und durch das Programm "awk" in Zeile 3 bis 12 weiter verarbeitet. Eventuell hast du nicht alles in der Zeile veröffentlicht, da die Variable "x" (in Zeile 5ff) nicht beschrieben istbzw später global mit einem boolschen Wert belegt wird. Da ich die Ausgabe der Programms nicht kenne, kann hier nur vermutet werden. In Zeile 4 bis 7 wird beschrieben, was passieren soll, wenn in der Zeile der String "SLot" auftaucht. Wenn "x" wahr ist (Zeile 5), wird x ausgegeben. Danach wird x auf "leer" gesetzt (Zeile 6). Das ist logische "False". Zeile 8 bis 10 beschreibt, was mit jeder Zeile an "awk" gemacht wird. In Zeile 9 wird überprüft, ob "x" nicht wahr ist (!x). Ist das der Fall, wird der Variablen x die eingelesenen Zeile ($0) zugewiesen. Wenn nicht wird bei der Zuweisung vor die eingelesene Zeile ($0) der Wert der Variablen "x" plus die Zeichenkette " | " vor gehängt. Diesen Trick wendet man an, wenn man mehrere Spalten ein eine Zeile Packen will, die durch " | " getrennt sind, damit sie später mit column hübsch ausgegeben werden können. Wenn alle Zeilen von "awk" verarbeitet wurden, wird der "END" Block (Zeile 11 bis 12) durchgeführt. Hierbei wird nur der Wert von "x" (Zeile 11) ausgegeben. Das bedeutet, die aus dem "egrep" gelieferten Zeilen werden als Spalten in einer Zeile aufbereitet ausgegeben. In den Zeilen 13 bis 15 löscht man auf umständliche Weise alle (g) Strings " Number, Count,Firmware stat: ". In Zeile 16 werden alle (g) Strings "Drive has flagged a S.M.A.R.T" durch "SMART" ersetzt. Am Ende (Zeile 17) erhält man die ausgegebenen Daten als Tabelle dargestellt.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 11947
|

14. Mai 2022 15:05
Eine Vereinfachung, die die Sache auch etwas effizienter macht, wäre, alle sed -Befehle in einen zu kombinieren: | sed -r 's/( Number| Count|Firmware state: )//g;s/Drive has flagged a S.M.A.R.T/SMART/g'
|
Man könnte auch das egrep in den awk falten: | awk '
/Slot/ {
if (x) print x;
x="";
}
/Slot|state|Error Count|flagged/ {
x=(!x)?$0:x " | " $0;
}
END {
print x;
}'
|
|
Nobodywolf
(Themenstarter)
Anmeldungsdatum: 25. September 2013
Beiträge: 7
|

14. Mai 2022 17:19
(zuletzt bearbeitet: 14. Mai 2022 17:24)
Das ist die kürzeste Version die noch funktioniert.
| megacli -PDlist -aALL -NoLog | awk '/Slot/{if (x)print x;x="";}/Slot|state|Error Count|flagged/{x=(!x)?$0:x " | " $0;}END{print x;}' | sed -r 's/( Number| Count|Firmware state: )//g;s/Drive has flagged a S.M.A.R.T/SMART/g' | column -t
|
Der Code ist vollständig, die Ausgabe sieht so aus: 1
2
3
4
5
6
7
8
9
10
11
12
13 | Slot: 0 | Media Error: 0 | Other Error: 6 | Online, Spun Up | SMART alert : No
Slot: 1 | Media Error: 0 | Other Error: 5 | Online, Spun Up | SMART alert : No
Slot: 2 | Media Error: 0 | Other Error: 5 | Online, Spun Up | SMART alert : No
Slot: 3 | Media Error: 0 | Other Error: 5 | Online, Spun Up | SMART alert : No
Slot: 4 | Media Error: 0 | Other Error: 5 | Online, Spun Up | SMART alert : No
Slot: 5 | Media Error: 0 | Other Error: 5 | Online, Spun Up | SMART alert : No
Slot: 6 | Media Error: 0 | Other Error: 1 | Online, Spun Up | SMART alert : No
Slot: 7 | Media Error: 0 | Other Error: 1 | Online, Spun Up | SMART alert : No
Slot: 8 | Media Error: 0 | Other Error: 13 | Online, Spun Up | SMART alert : No
Slot: 9 | Media Error: 0 | Other Error: 2 | Online, Spun Up | SMART alert : No
Slot: 10 | Media Error: 0 | Other Error: 3 | Online, Spun Up | SMART alert : No
Slot: 11 | Media Error: 0 | Other Error: 3 | Online, Spun Up | SMART alert : No
Slot: 12 | Media Error: 0 | Other Error: 0 | Online, Spun Up | SMART alert : No
|
Kann man den sed Teil mit awk schreiben oder den awk Teil in sed? Das END{print x;} kann man offenbar weglassen und dies Ausgabe bleibt gleich.
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 405
|

14. Mai 2022 19:19
(zuletzt bearbeitet: 14. Mai 2022 19:46)
Das END{print x;} kann man offenbar weglassen und dies Ausgabe bleibt gleich.
Das ist nicht ganz korrekt. Immer, wenn ein neuer Datenblock beginnt, der durch die Zeile mit dem String "Slot" markiert ist, ist die Variable x gefüllt (also logisch "true"). Diese wird beim "/Slot/" Block ausgegeben (if (x)print x;). Die letzte Zeile wird allerdings nur durch den "END" Block ausgegeben, da ja keine neue Zeile mit "Slot" mehr folgt.
Kann man den sed Teil mit awk schreiben oder den awk Teil in sed?
Ja, statt die Aktionen mit sed zu machen, kannst du auch in awk "gsub" verwenden (z.B. einfach gsub(" Number","",x) for dem print im "/Slot/" und "END" Block (if (x)print x;).
statt:
if (x)print x;
besser:
if (x){gsub(" Number| Count|Firmware state: ","",x);gsub("Drive has flagged a S.M.A.R.T","SMART",x);print x};
Da die Substitutionen 2 mal auftauchen (beim /Slot/ und beim END Block), macht es Sinn, diese in eine Funktion zu packen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | megacli -PDlist -aALL -NoLog |
awk '
function clean(xx){
gsub(" Number| Count|Firmware state: ","",xx);
gsub("Drive has flagged a S.M.A.R.T","SMART",xx);
return xx;
}
/Slot/ {
if (x)print clean(x);
x="";
}
/Slot|state|Error Count|flagged/{
x=(!x)?$0:x " | " $0;
}
END {
print clean(x);
}' | column -t
|
|
Nobodywolf
(Themenstarter)
Anmeldungsdatum: 25. September 2013
Beiträge: 7
|

14. Mai 2022 19:28
Danke shiro, Du hast recht.
Slot 12 war ohne "END" Block nicht aufgeführt, danke.
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 405
|

14. Mai 2022 22:14
(zuletzt bearbeitet: 14. Mai 2022 22:30)
Der Vollständigkeit halber nun die Lösung nur mit sed:
| megacli -PDlist -aALL -NoLog | egrep 'Slot|state|Error Count|flagged' | sed '$ a\ Slot' |
sed -r -n '/Slot/ {:c;H;:b;n;s/Slot/Slot/;ta;bc;:a;x;s/\n/ | /g;s/^ \| //;s/( Number| Count|Firmware state: )//g;s/Drive has flagged a S.M.A.R.T/SMART/gp;g;bb}' | column -t
|
Da sed keinen "END" Block kennt, muss man an das Ende der Datei noch eine Zeile mit dem Marker eines neuen Blocks anfügen (sed '$ a\ Slot'). Für einen Ungeübten lesen sich sed Scripts dieser Komplexität immer etwas kryptisch. Es werden die Labels "c", "b" und "a" verwendet. Ab dem Record, der "Slot" enthält beginnt die Verarbeitung und die eingelesene Zeile wird in den Hold-Space appended und dann die nächste Zeile eingelesen. Dann wird "Slot" mit sich selbst ersetzt. Ist das erfolgreich, haben wir einen neuen Datenblock und hüpfen zum Label "a" zur Nachverarbeitung, sonst an den Anfang zu Label "c". Ab Label "a" wird der Hold-Space in den Pattern-Space geswapped (x) und alle Zeilenumbrüche durch " | " ersetzt. Das erste " | " wird entfernt und die bekannten Löschungen und SMART Ersetzung durchgeführt. Das Ergebnis wird ausgegeben (p) und die zuvor gemerkte Zeile mit dem neuen Slot Block in den Pattern-Space kopiert (g). Zur weiteren Verarbeitung hüpft man wieder zu Label "b". Wenn nichts mehr von STDIN gelesen wird, ist die Arbeit von sed beendet und "column" zeigt alles schön an. Ich hab der Übersichtlichkeit halber immer schön ";" gesetzt. Das kann man auch noch optimieren.
|
Nobodywolf
(Themenstarter)
Anmeldungsdatum: 25. September 2013
Beiträge: 7
|

14. Mai 2022 23:11
Wow, shiro
Anfangs dachte ich sed wäre einfacher zu verstehen als awk, jedoch bei dieser kryptischen Lösung komme ich nicht mehr mit.
Habe mich gerade gefragt, beide Lösungen schauen zuerst auf den Eintrag "Slot", geht das nicht auch anders?
Irgend sowas wie, das eine definierte Anzahl von Zeilen (in diesem Fall 4) in Spalten gezeigt, bzw. geschrieben werden?
Oder noch unabhängiger, das Script beginnt mit dem ersten Ausdruck, sobald dieser wieder erscheint wird eine neue Zeile begonnen.
Geht sowas? Oder ist dies noch viel komplexer?
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: Zähle...
Wohnort: München
|

15. Mai 2022 00:53
Man kann das expliziter lösen, z.B. mit Python3 (>= 3.8):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | #!/usr/bin/env python3
import fileinput
from collections import defaultdict
fields_of_interest = {
"Slot Number": "Slot",
"Media Error Count": "Media Error",
"Other Error Count": "Other Error",
"Firmware state": "",
"Drive has flagged a S.M.A.R.T alert": "SMART alert",
}
data = {}
output_lines = []
longest_fields = defaultdict(int)
for line in fileinput.input():
if not (line := line.strip()) or len(fields := line.split(':', maxsplit=1)) != 2:
continue
key, value = fields
data[key.strip()] = value.strip()
if all(f in data for f in fields_of_interest):
output = []
for k in fields_of_interest:
output.append((k, data[k]))
longest_fields[k] = max(len(data[k]), longest_fields[k])
data.clear()
output_lines.append(output)
for output in output_lines:
print(' | '.join((f"{(n := fields_of_interest[k])}{': ' if n else ''}{v.rjust(longest_fields[k])}" for k, v in output)))
|
megacli -PDlist -aALL -NoLog | ./megacli_parser.py
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 405
|

15. Mai 2022 11:02
Nobodywolf schrieb: Geht sowas? Oder ist dies noch viel komplexer?
Ja, das geht natürlich es wird dabei aber nicht wesentlich einfacher (je nachdem eher komplexer).
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 11947
|

16. Mai 2022 13:51
Es wäre wirklich hilfreich, wenn Du mal eine Beispielausgabe von megacli -PDlist -aALL -NoLog postest zusammen mit der Beschreibung, was Du da rausbekommen möchtest. Ggf. kommen dann ganz neue Ideen / Vorschläge.
|