Gibt es denn immer eine Kombination die klappt?
Was passiert wenn an einem Tag nur zwei 2en stehen oder nur zwei 7en?
Welche Vorschrift gilt denn um die "richtige" Kombination auszuwählen?
Mir sind auch nicht klar wie die Lücken in deinem $aResultValue entstehen:
Beim 05.10. ist die Lücke an letzter Stelle, was ich mir erkläre, dass einfach die an der Kombination beteiligten nacheinander eingetragen wurden.
Dann aber wieder ist die Lücke beim 11.10. gleich am Anfang.
Hier bedarf es sicher noch besserer Erklärung und vor allem weiterer konkreter Beispiele.
Beiträge von AspirinJunkie
-
-
Worum geht es?
In AutoIt haben wir es oft mit tabellenartig strukturierten Daten zu tun.
Je nach Quelle müssen wir teils einen erheblichen Codeaufwand betreiben um die Daten in eine Form zu bringen, mit der wir weiterarbeiten können.
Anschließend wird das Arbeiten mit diesen Daten auch nicht unbedingt einfacher, da wir, anstatt auf die sprechenden Namen der Attribute der Daten zuzugreifen, wir es mit numerischen Indizes zu tun haben bei denen man sehr schnell die Übersicht verliert.Beide Probleme werden durch die UDF adressiert.
Im Grundansatz arbeitet die UDF mit einem Table-Objekt (lediglich eine AutoIt-Map), in welcher die Daten vom Header getrennt werden.
Das ermöglicht eine sauberere Prozessierung der Daten.
Sie bietet Funktionen um aus verschiedenen Quellen Daten einzulesen (CSV, Array, string with fixed-width-columns, Strings mit eigenen Spaltentrennungen).
Innerhalb dieses Aufrufes werden die Daten vom Header getrennt oder diesen überhaupt erst Headerdaten hinzugefügt.
Die Daten werden dann entsprechend ihrem Format aufgearbeitet (bei csv Quotes und Escapes entfernt, bei fixed-width Leerräume entfernt...).
Darüber hinaus kann der User direkt für jede Spalte einzeln festlegen wie genau die Daten aufgearbeitet werden sollen. Er ist hierbei komplett frei.
Auf diese Art erhalten die Daten bereits beim Einlesen das Endformat in welchem sie weiterverarbeitet werden sollen.Anschließend können die Daten spaltenbasiert oder Attributbasiert behandelt werden.
Sprich: Anstatt mit Indizes können auch einfach die Attributnamen der Daten verwendet werden - das ermöglicht eine bessere Übersichtlichkeit im Code.Beispiel (umfangreiche weitere Beispiele im Unterordner "examples")
Wir möchten die offenen Ports auf einem Rechner mit AutoIt auswerten.
Der Kommandozeilenbefehl lautet hierfür netstat -t und bringt uns folgende Ausgabe (bei euch je nach System evtl. anders strukturiert):CodeActive Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 116 192.168.64.110:ssh 192.168.29.200:65069 ESTABLISHED tcp 0 0 192.168.64.110:ssh 192.168.29.200:65068 ESTABLISHED
Um diese Daten sinnvoll weiterzuverarbeiten müssen wir gegebenenfalls folgende Schritte in AutoIt durchführen:
- Löschen der ersten unnützen Zeilen
- Extraktion der Kopfzeile (sie wird anders behandelt als die Daten)
- Erzeugung eines Arrays mit korrekter Dimension um die Daten zu halten
- Trennung der Daten entweder anhand von Leerräumen oder anhand der jeweils festen Spaltenbreite
- Befreiung der Daten von unnötigen Leerräumen
- Konvertierung der Recv-Q und Send-Q-Spalte in einen Zahlendatentyp (für Sortierung usw)
- Auftrennung der Adressdaten in IP-Adresse und Port
Das kann eine Menge (fehleranfälligen) Code-Aufwand bedeuten.
Mit dieser UDF hingegen kann man das ganze folgendermaßen in einem einzigen Aufruf lösen:AutoIt
Alles anzeigen$sString = 'Active Internet connections (w/o servers)' & @CRLF & _ '' & @CRLF & _ 'Proto Recv-Q Send-Q Local Address Foreign Address State' & @CRLF & _ 'tcp 0 116 192.168.64.110:ssh 192.168.29.200:65069 ESTABLISHED' & @CRLF & _ 'tcp 0 0 192.168.64.110:ssh 192.168.29.200:65068 ESTABLISHED' ; Transfer data to table type by using the size of every column: Global $mData = _td_fromFixWidth($sString, _ "left 6; Number 7; Number 7; StringSplit($x, ':', 2) 24;StringSplit($x, ':', 2) 24;", _ ; column definitions "1-2", _ ; skip row 1 to 2 True) ; first row = header ; display data _td_display($mData)
und wir erhalten als Resultat:
Wenn man nun Funktionen wie _td_toObjects() nutzt, dann kann man die einzelnen Daten mit Ausdrücken wie $aData.Proto oder $aData.State prozessieren.
Das sollte deutlich übersichtlicher sein als sich mit den Arrayindizes herumzuschlagen.Funktionsumfang
Funktion
Beschreibung
Eingabe
_td_fromString konvertiert einen Tabellenartig strukturierten String, bei welchem die Spaltentrenner über einen regulären Ausdruck beschrieben werden können, in ein Tabellenobjekt _td_fromCsv Liest eine Datei oder String in ein Tabellenobjekt, welcher als csv, tsv oder ähnlichem (anpassbaren) Format strukturiert ist. _td_fromFixWidth Konvertiert einen tabellenartig strukturierten String in ein Tabellenobjekt, bei dem die Spalten feste Zeichenbreiten haben (üblicherweise Konsolenausgaben oder printf-Tabellen) _td_fromArray Erzeugt ein Tabellenobjekt aus einem vorhandenen Array Ausgabe
_td_toCsv Konvertiert ein Tabellenobjekt in einen csv/tsv-formatierten String _td_toFixWidth Konvertiert ein Tabellenobjekt in einen String, in welchem die Spalten feste Breiten haben _td_display Stellt ein Tabellenobjekt ähnlich _ArrayDisplay dar (mit Headerbezeichnern und Sortiermöglichkeit) _td_toArray Konvertiert ein Tabellenobjekt in ein 2D-Array bei welchem der Header in der ersten Zeile steht (wenn man nur die Daten haben möchte stattdessen $mTable.Data nutzen) Bearbeitung von Tabellen
_td_join Verknüpft 2 Tabellenobjekte ähnlich wie ein SQL-Join _td_filter Filtert Datensätze in Tabellenobjekten ähnlich zu einem SQL-Where Aufbereitung zur effizienteren Weiterbehandlung
_td_toObjects Konvertiert ein Tabellenobjekt in eine Liste von Datenobjekten. Jeder Eintrag ist damit eine Map bei welcher die Attribute über den Namen angesprochen werden können. _td_MapsToTable Konvertiert eine Liste von Maps (wie von _td_TableToMaps) in ein 2D-Array _td_toPrimaryKeys Konvertiert ein Tabellenobjekt in eine Map für die Datensätze, bei welcher ein Primärschlüsselattribut der Daten als Key verwendet wird. (ermöglicht Zugriff auf die Daten mit Namen anstatt Indizes) _td_toColumns Konvertiert ein Tabellobjekt in eine Map, welche als Einträge die einzelnen Spalten als 1D-Array enthalten. _td_getColumn Extrahiert eine Spalte aus einem Tabellenobjekt -
Ich kann Dir nicht sagen ob das in der Windowswelt die 8 Byte schon abgerechnet werden oder einfach nur anders angezeigt.
Werden sie nicht.
Können sie auch nicht, da der Ping-Parameter "-l" die gewünschte Größe des Sendepuffers angibt - was in einem ICMP-Paket die Nutzlast ist.
Dieser ist durch die MTU und die Header des Pings (ICMP-Header + IP-Header) begrenzt.
Würde der Windows-Ping frech einfach an dieser Stelle 28 Byte hier runterrechnen wäre der Parameter falsch, da er nicht mehr die Größe der Nutzdaten definiert sondern irgendetwas anderes (den Parameter gibt es nicht für die Ermittlung der MTU - er wird hierfür lediglich missbraucht)
Er würde sogar undefinierte Größen im Bereich 0 bis 27 erzeugen. (negative Angaben...)
Hab es auch gerade hier getestet bei einer definitiven MTU von 1500: ein Ping mit -l 1473 versagt zuverlässig. -
Daher Teste ich immer mit dem ping ziel -f -l 1500....wenn es passt OK,
Das kann dann aber nur in einem Netz mit aktivierten Jumboframes passen.
Bei normalen Ethernet sollte dieser Aufruf immer versagen.
Selbst bei minimaler Funktionalität im Ethernetframe kommt man nicht auf über 1.500 MTU.
Der Aufruf checkt aber hingegen auf eine MTU von 1528 Bytes.
Oder anders ausgedrückt: Wenn ping ziel -f -l 1472 noch durchgeht - dann hast du eine MTU von 1.500 Bytes. -
Ob die MTU zu groß eingestellt ist kannst Du sehr einfach feststellen: ping meinserver.com -f -l 1500
Damit würdest du doch aber auf eine MTU von 1528 testen - oder?
Um zu prüfen ob im Netz eine MTU von 1500 Bytes funktioniert müsste man ja noch IP-Header (20 Byte) und den ICMP-Header (8 Byte) abziehen.
Also wäre der korrekte Befehl um auf eine MTU von 1500 zu prüfen folgender: ping meinserver.com -f -l 1472 -
Was für ein Timeout?
-
Für die gängigen JSON-UDFs wäre dies der Pfad für die direkte Abfrage: data.403240.depots.branches.public.buildid
-
Vielleicht sind an diesen Stellen nicht darstellbare Steuerzeichen oder soetwas.
Ich hätte spontan versucht mir die ASCII-Codes anzeigen zu lassen um zu schauen ob da irgendein Zeichen noch dazwischen ist:
Wichtig: Skript muss hierfür in diesem Ordner liegen (Pfad manuell eintragen nützt da nichts):Oder vielleicht noch so:
-
Na gut bei so vielen Lösungen wäre hier noch die von mir angesprochene Lösung wie man mit _ArrayFindAll() rangehen könnte:
AutoIt
Alles anzeigen#include <Array.au3> Global $aIndices Global $aArray[5][5] = [[0, "Test1", 27, 1, 0], _ [1, "Test1", 27, 4, 2], _ [2, "Test2", 28, 1, 3], _ [3, "Test3", 29, 1, 0], _ [4, "Test4", 29, 4, 1]] ; die Suchwerte extrahieren Global $aSearchValues = _ArrayExtract($aArray, -1, -1, 2, 2) $aSearchValues = _ArrayUnique($aSearchValues, 0, 0, 0, 0) ; Die Suchwerte einzeln durchgehen und ; das Array nach dem jeweiligen Suchwert filtern For $dSearchValue In $aSearchValues ; Indizes der Zeilen mit dem aktuellen Suchwert ermitteln $aIndices = _ArrayFindAll($aArray, $dSearchValue, 0,0,0,0,2) ; Neues 2D-Array mit den Ergebniszeilen erstellen Global $aFiltered[UBound($aIndices)][UBound($aArray, 2)] For $i = 0 To UBound($aFiltered) - 1 For $j = 0 To UBound($aArray, 2) - 1 $aFiltered[$i][$j] = $aArray[$aIndices[$i]][$j] Next Next ; gefiltertes Array ausgeben: _ArrayDisplay($aFiltered, "Nr. " & $dSearchValue, "", 96) Next
-
Ich bin nicht 100% sicher ob ich die Aufgabe genau verstanden habe.
So wie ich das verstanden habe möchtest du aus diesem kompletten Array für die jeweiligen Werte in Spalte 3 eigene Arrays machen?
Das geht schon mit Bordmitteln.
Bedenke aber dass _ArrayFindAll nur die Indizes wiedergibt.
Das extrahieren dieser Zeilen und den Aufbau eines neuen Arrays musst du dann noch hinterher programmieren.Das geht - aber für sowas habe ich mal eine UDF (>>ArrayPlus-UDF<<) mit der man sich soetwas deutlich einfacher basteln kann.
Hier mal als Beispiel wie man damit deine Aufgabe umsetzen könnte:AutoIt
Alles anzeigen#include <ArrayPlus.au3> Local $aArray[5][5] = [[0, "Test1", 27, 1, 0], _ [1, "Test1", 27, 4, 2], _ [2, "Test2", 28, 1, 3], _ [3, "Test3", 29, 1, 0], _ [4, "Test4", 29, 4, 1]] ;~ _ArrayDisplay($aArray, "2D Array") ; die Suchwerte extrahieren Global $aSearchValues = _ArraySlice($aArray, "[:][2]") $aSearchValues = _ArrayUnique($aSearchValues, 0, 0, 0, 0) _ArrayDisplay($aSearchValues, "Suchwerte") ; Die Suchwerte einzeln durchgen und ; das Array nach dem jeweiligen Suchwert filtern For $dSearchValue In $aSearchValues $aResult = _ArrayFilter($aArray, "$A[2] = " & $dSearchValue) _ArrayDisplay($aResult, "Nr. " & $dSearchValue, "", 96) Next
-
Die 6006 war jetzt nur das erstbeste Beispiel. Es gibt ja weitere Event-IDs welche man zum herunterfahren abfragen kann. Einfach probieren welche ID am zuverlässigsten funktioniert.
Wenn zwischendurch neu gestartet wurde, dann kann man ja die Schleife weiterführen und die jeweils davor liegenden Events ausgeben.
-
Da der Eventlog ja offensichtlich das korrekte Ergebnis bringt, könntest du ja stattdessen eben diesen abfragen?`
Bin jetzt kein Experte für den Eventlog aber vielleicht soetwas?:AutoIt
Alles anzeigen#include <EventLog.au3> #include <Array.au3> Global $hEventLog = _EventLog__Open("", "System") Global $i = 0, $aEvent Do $aEvent = _EventLog__Read($hEventLog, True, False, $i) If $aEvent[0] = False Then ExitLoop If $aEvent[6] = 6006 Then _ArrayDisplay($aEvent, "Das letzte mal heruntergefahren") ExitLoop EndIf $i += 1 Until 0 _EventLog__Close($hEventLog)
-
Und auch hier wieder: Ich habe das nicht vorhergesehen.
Erst mit komischen Testdaten stößt man auf solche Besonderheiten. -
Ich hab das schon verstanden, die Datei ist ja nur Quelle als Namensgeber, mehr nicht.
Beispiel:
Dateiname: da_block.png.test
Resultierender Ordnername: da_block.pngDas kann ich nicht nachvollziehen mit meinem Skript (dort ist die Prüfung ja drin ob der entsprechende Ordner bereits existiert).
[...] nachvollziehen kann ich deine obige Aussage jedoch nicht.Bis hierhin ist ja wie gesagt auch alles korrekt so wie es sein soll. Bis hierhin hast du ja keine Ordner mit Punkten im Namen drin.
Nun nach Skriptausführung hast du jedoch einen Ordner(!) namens "da_block.png" im Verzeichnis.
Nun führe dein Skript erneut aus.
Resultat: Nun wird ein Ordner "da_block" erzeugt und es kommt folgendes Phänomen von FileMove() zum Tragen:
Werden als Quelle und Ziel zwei vorhandene Ordnernamen angegeben, dann werden alle Dateien aus dem Quellordner in den Zielordner verschoben.Das heißt: Nun hast du einen Ordner namens "da_block", in welchem die Datei da_block.png.test liegt. Zusätzlich hast du einen leeren Ordner namens da_block.png.
Stattdessen war das gewünschte Ziel: Ein Ordner namens da_block.png in welchem die Datei da_block.png.test liegt.
Um das zu lösen musst du wie mehrmals gesagt, Ordner von der Prozessierung ausschließen.
Edit: Vielleicht verstehe ich langsam worauf du hinausmöchtest: Nur allein mit dem 3. FileMove-Parameter löst man das Problem tatsächlich nicht. Es bedarf zusätzlich der Abfrage ob es sich um einen Ordner handelt (siehe mein Skript oben). -
Ordner - nicht Datei.
Wenn du einen Ordner mit dem Namen Mein.Ordner im Verzeichnis hast (z.b. bei einem erneuten Durchlauf), dann wird ein leerer Ordner namens Mein erzeugt.
Umgehen kann man dies, wie oben vorgeschlagen, durch einen Extra-Check ob Ordner gefunden wurden oder auch durch das Weglassen der DirCreate-Zeile und stattdessen dem Parameter bei FileMove. -
Und bei meinenm Beispiel Code war die Prüfung ob ein Ordner existiert bereits drin (sonst wird eben keiner erstellet, nur verschoben; Zeile 21).
War das an mich adressiert?
Mein Punkt war, dass eben diese ganze Zeile gar nicht notwendig ist, da FileMove() diese Funktionalität gleich mitbringt.
Einfach eine 8 als 3. Parameter eintragen und fertig.
Das würde auch ein weiteres Randphänomen in deinem Skript mit beseitigen:
Erstelle mal einen Ordner (keine Datei) mit dem Namen Mein.Ordner und schau was mit der DirCreate-Zeile daraus gemacht wird.Aber das man vor dem . ein Leerzeichen haben könnte habe ich in der Tat nicht bedacht...auf so eine Idee würde ich nie kommen
Ich ebenso niemals. Deshalb hab ich ja so auf die konkreten Namen gedrängt.
Man versucht ja immer jeden Randfall gleich mit zu bedenken aber es wird immer Fälle geben, an die man eben nicht gedacht hat.
Daher kommt kein Programm ohne anschließende Testung aus. -
Nein - die Klammern sind überhaupt nicht dein Problem.
Das Problem ist das Leerzeichen vor dem Punkt.
Ordnernamen dürfen nicht auf Leerzeichen enden, deswegen wird ein Ordner ohne abschließendes Leerzeichen erzeugt aber der FileMove-Befehl schläg fehl, da dort das Leerzeichen noch drin steht.
Diesen Fall muss man also bei der Erzeugung des Zielordnernamens noch mit beachten:AutoIt
Alles anzeigenGlobal $sFileName = "", $sDir Global $hSearch = FileFindFirstFile(@ScriptDir & "\*") If $hSearch = -1 Then Exit MsgBox(4096, "", "Error: No files/directories matched the search pattern.") Do $sFileName = FileFindNextFile($hSearch) If @error Then ExitLoop If @extended Or ($sFileName = @ScriptName) Then ContinueLoop $sDir = @ScriptDir & '\' & StringRegExpReplace($sFileName, '\h*\.[^.]+$', '') & '\' & $sFileName FileMove(@ScriptDir & '\' & $sFileName, $sDir, 9) Until 0 FileClose($hSearch)
Und jetzt verstehst du hoffentlich auch mal warum wir ganz konkret nach den tatsächlichen Dateinamen fragen.
Wir fragen nicht zum Spaß. -
Meine Gedanken dazu (die alle nicht das beschriebene Problem betreffen):
- Aktuell werden auch Ordner erkannt und prozessiert (ja das Pattern *.* bei FileFindFirstFile ändert daran nichts...).
Das fällt erst einmal nicht auf, da die Funktionen wie FileMove hiermit scheitern aber besser wäre es dennoch bei FileFindNextFile mit @extended prüfen ob es sich um Dateien handelt. - Das DirCreate() kann man sich sparen, wenn man beim FileMove() $FC_CREATEPATH setzt.
- Das Pattern kann auch so angepasst werden, dass es sowohl für Dateien mit Endung, als auch ohne Endung funktioniert:
- $iResult wird deklariert und definiert aber nie verwendet.
Ansonsten schlage ich in die selbe Kerbe wie Musashi : Um das zu debuggen musst du uns das komplett unangetastete Skript zeigen, welches den Fehler erzeugt und die tatsächlichen (keine anonymisierten) Namen der Dateien in dem Ordner auflisten.
- Aktuell werden auch Ordner erkannt und prozessiert (ja das Pattern *.* bei FileFindFirstFile ändert daran nichts...).
-
Bleibt noch die Möglichkeit, wenn Punkte im Dateinamen verwendet wurden (z.B. test.txt.old)
Deshalb schrieb ich ja: "den letzten Punkt". Mit StringInStr() machbar indem man den occurance-Parameter auf -1 setzt.
StringSplit braucht man hierfür nicht und kann es in einem Einzeiler erledigen.
-
Moombas aufpassen: Dateiendungen müssen nicht 3 Zeichen lang sein (z.b. 7z, jpeg etc.).
Daher lieber die Position des letzten Punktes im Dateinamen mit StringInStr() ermitteln und diese als Größenangabe beim splitten verwenden.