Dateien nach Alter/Bearbeitungszeit/Zugriffszeit auflisten.

  • Hi Leute,

    Ich habe hier mal ein Skript, das Dateien, die ein bestimmtes min/max Alter haben, auflistet. UDF wäre wohl zu viel, auch wenn ich einen entsprechenden Header erstellt habe. Kritik wird gerne angenommen.


    EDIT: Thread weiter lesen. Unten finden sich bessere Funktionen!

    Grüße autoiter

    Einmal editiert, zuletzt von autoiter (7. Februar 2017 um 20:20)

  • Very nice! ;)

    Im Header fehlt bei "iFullPath" das "$"

    $iAgeAndOlder - 0 = all files .................... ok
    $iAgeAndOlder - 1 = all files older than $iAge ... $iDateCalcDay >= $iAge --> all files older than $iAge or the same as $iAge
    $iAgeAndOlder - 2 = all files newer than $iAge ... $iDateCalcDay <= $iAge --> all files newer than $iAge or the same as $iAge

    Hier muss < 0 hin.. nicht = 0

    AutoIt
    If Number($iAge) < 0 Then Return SetError(1, "", "Invalid value for $iAge")

    Hier fragst du 2x "m" ab... das 2-te m muss ein n sein...

    AutoIt
    If $sUnit <> "d" And $sUnit <> "y" And $sUnit <> "m" And $sUnit <> "w" And $sUnit <> "h" And $sUnit <> "m" And $sUnit <> "s" Then ConsoleWrite("Invalid unit" & @CRLF)

    Hm, die drei Funktionen sind ja fast identisch...

    __ListFilesByAge_AllFiles() ; hier wird $iAge als Parameter mit angegeben, in der Funktion aber nicht genutzt...
    __ListFilesByAge_OldFiles()
    __ListFilesByAge_NewFiles()

    somit lässt sich das auch leicht in einer Funktion erledigen, womit du das Script drastisch verkürzen kannst...

    __ListFilesByAge()
    __ListFilesByAge_Example()

    13 Mal editiert, zuletzt von Bitnugger (7. Februar 2017 um 10:18)

  • Hallo @Bitnugger,
    Super. Freut mich, dass du dir die Funktion mal genauer angeschaut hast. Danke dir für die Korrekturen :)

    If Number($iAge) = 0 Then Return SetError(1, "", "Invalid value for $iAge")

    Die Prüfung würde ich so lassen. Number() gibt ja u.U. auch bei ("d") eine 0 aus.

    Bei $iAgeAndOlder sollte es reichen den gültigen Parameter für alle Dateien auf -1 ändern. Das hatte ich auch so. Als ich aber den Header bearbeitet habe, dachte ich plötzlich, das sieht ja doof aus und habe ohne darüber nachzudenken die -1 in 0 geändert. (extra nur älter und älter und gleich alt usw. ist eher unnötig, oder?)

    (EDIT: Das obere habe ich bearbeitet. Da hatte ich mich bei den eigenen Parametern vverhaspelt :D Mir ist auch gerade aufgefallen, dass bei $iAge noch der Text aus der UDF steht, aus der ich den Header ursprünglich kopiert habe..)

    Diskutieren möchte ich aber vor Allem die Kürzung des Codes. Damit habe ich gerechnet, wenn auch nicht von dir. :D
    Da kommen wir nämlich zu meiner Schwäche, dass mir theoretische Grundlagen und Verständnis fehlen.

    Ich hatte auch alles in einer Funktion. Dann habe ich mir den Part angeschaut und gedacht, das ist doch ineffizient und habe den schlechten Stil gewählt. In meinem Ansatz frage ich einmal nach dem Wert der Variable $iAgeAndOlder und rufe die entsprechende Funktion auf. Du prüfst 5, 500 oder >50.000 mal den Wert der Variable (gerade bei rekursiver Auflistung).

    Stattdessen übergebe ich zwischen den Funktionen aber 2 mal Arrays, wo ich nicht weiß, wie viel Zeit das kostet.

    Wolltest du es nur Kürzen, oder sollte man zur besseren Übersicht so kurz halten?
    Spielen meine Überlegungen keine praktische Rolle (weil zu kleiner Zeitfaktor)?

    Grüße autoiter

    Einmal editiert, zuletzt von autoiter (7. Februar 2017 um 07:17) aus folgendem Grund: Korrektur

  • Ich habe die Scripts in #Post 2 noch mal stark geändert und noch ein paar Fehler korrigiert...

    Die Prüfung würde ich so lassen und stattdessen den gültigen Parameter für alle Dateien auf -1 ändern.

    Die Prüfung hat doch nichts mit dem Parameter für alle Dateien zu tun... $iAge musst du auf 0 setzen, weil 0 Tage ein gülter Wert ist und du sonst ja nicht danach suchen kannst. Die Abfragen habe ich alle noch mal geändert...

    Diskutieren möchte ich aber vor Allem die Kürzung des Codes.

    Ja gerne doch...

    Du prüfst 5, 500 oder >50.000 mal den Wert der Variable (gerade bei rekursiver Auflistung).

    Jetzt zwar nicht mehr, aber das Abfragen der Variablen geht sicher weitaus schneller, als das Kopieren von großen Arrays... wobei du hier auch wieder das ByRef vergessen hast.

    Wolltest du es nur kürzen, oder sollte man zur besseren Übersicht so kurz halten?

    Hihi... nein, nicht nur kürzen... und jetzt nicht lachen, aber ich war auch dabei mir so eine Funktion zu tippern... und dann sehe ich dann dein Script hier. :D

    Seine Programme sollte man nicht nur wegen der besseren Übersicht möglichst kurz halten... denn je größer sie werden, umso zeitraubender wird jede Aktion... das Scrollen im Code, die Fehlersuche, das Starten/Laden... und umso mehr Fehler schleichen sich auch ein. Oft ist es so, dass es mehr Zeit und Hirnschmalz braucht, ein kurzes Programm zu schreiben und man dabei auch schneller in Schräglage geraten kann, da man innerhalb einer kompakten Funktion auf mehr Dinge achten muss, als wenn man für jedes mutierte Problem eine eigene Funktion anlegt. Kurze Programme sind vor allem leichter zu pflegen, lange dafür meist schneller. Ich denke allerdings, dass auch hier die ultimative Grundregel gilt und man den optimalen Punkt in der goldenen Mitte findet.

    Einmal editiert, zuletzt von Bitnugger (9. Februar 2017 um 05:40)

  • Die Prüfung hat doch nichts mit dem Parameter für alle Dateien zu tun... $iAge musst du auf 0 setzen, weil 0 Tage ein gülter Wert ist und du sonst ja nicht danach suchen kannst. Die Abfragen habe alle noch mal geändert...

    Ja, ich war heute morgen noch nicht richtig wach. Vor dem ersten Kaffee geht eigentlich nichts. Ich war aber so aufgeregt, dass jemand sich das angesehen hat, dass ich sofort was schreiben wollte ^^
    Das war gestern auch ein Schuss aus der Hüfte. Eigentlich hätte ich das erst einmal liegen lassen müssen und mir heute nochmal genau ansehen sollen. Ich bin eindeutig nicht gut genug, dass ich guten, fehlerfreien Code am Stück produziere..

    Wirklich witzig, dass du das auch gerade machen wolltest :D So ist es schön zu sehen, wie du bestimmte Sachen angehen würdest.

    Grüße autoiter

  • Hi,

    AutoIt
    $aFileTime = FileGetTime($aFiles[$i], $iTimestampType)
    $sFileTime = $aFileTime[0] & "/" & $aFileTime[1] & "/" & $aFileTime[2] & " " & $aFileTime[3] & ":" & $aFileTime[4] & ":" & $aFileTime[5]

    Hier sollte besser nochmal überprüft werden ob $aFileTime überhaupt ein Array ist.
    Wenn jemand noch am PC Arbeitet, wärend das Script läuft kann es passieren, dass Dateien gelöscht werden [Edit: nur zu verständlichkeit: nicht von deiner Funktion sondern vom Nützer oder anderen Anwendungen] (z.B. wird beim schließen eines Word Dokuments eine versteckte Datei gelöscht).
    Da diese zwischenzeitlich gelöschten Dateien immernoch in dem von _FileListToArrayRec() erstellten Array enthalten sind wird trotzdem versucht sie über FileGetTime abzufragen, was dann in Zeile 106/122/141 zu einem Fehler führt.

    @Bitnugger:

    AutoIt
    $aReturnedFiles[$i][0] = DllCall($hDll, 'none', 'PathStripPathW', 'wstr', $aReturnedFiles[$i][0])[1]


    Hier würdest du mit RegExpReplace vermutlich deutlich schneller sein, statt ständig DllCall zu benützen.

    Beim gesamten Konzept frage ich mich ob eine Funktion, welche Dateien auf einem frei bestimmbaren Zeitraum überprüft nicht deutlich sinnvoller wäre.
    Also anstatt den Zeitraum über $iAge und $iAgeAndOlder festzulegen einfach $sStartDate und $sEndDate zu verwenden.
    Habe schnell mal hierzu auch mal was zusammen gebastelt, ist allerdings "quick and dirty".

    Spoiler anzeigen

    mfg
    Zeitriss

    2 Mal editiert, zuletzt von Zeitriss (7. Februar 2017 um 19:46)

  • Hey @Bitnugger,
    mittlerweile habe ich deine Variante mal genauer angeschaut. Viel cooles Zeug für mich dabei! :)


    StringFormat finde ich sehr schön. Ich benutze das viel zu selten und hatte das nicht auf dem Radar, obwohl ich es bei dir schon oft gesehen habe und auch @Zeitriss mir da auch schon mal damit etwas gezeigt hat. Selbst habe ich das nur auf dem Radar, wenn ich mal führende Nullen möchte :D

    AutoIt
    $sFileTime = StringFormat('%s/%s/%s %s:%s', $aFileTime[0], $aFileTime[1], $aFileTime[2], $aFileTime[3], $aFileTime[4], $aFileTime[5])

    Die DLL-Variante in der Funktion __ListFilesByAge_DeletePaths($aReturnedFiles) finde ich interessant. Ich habe es mehrfach mit 10.000 Dateien getestet. Die DLL-Variante ist immer etwas schneller gewesen. In dem Fall zwar nicht die Welt, aber dennoch zu bevorzugen. Wie bist du darauf (shlwapi.dll) gekommen?


    Am geilsten finde ich aber die Geschichte mit den $iAgeAndOlder Fällen!

    AutoIt
    Local Static $aFx = [_F0, _F1, _F2, _F3, _F4]
    ...

    Ich hatte keine Ahnung, dass so eine Variante funktionieren kann! Woher hast du das??? :D
    Das werde ich mir auf jeden Fall merken.


    Bei den Parameter-Prüfungen finde ich neben IsInt das StringReplace interessant.

    AutoIt
    If StringReplace("dymwhns", $sUnit, '') And @extended <> 1 Then Return SetError(4, "", "Invalid unit")

    Streng genommen lässt das noch nicht abgefangene "ym" oder so einen Quatsch zu, aber auch das finde ich im Vergleich zu meiner ewig langen If .. And .. And-Kette eine gewitzte Verbesserung.

    Bei den Bedingungen gibt es noch einen Fehler.

    AutoIt
    If $iTimestampType <> 0 Or $iTimestampType > 2 Then Return SetError(5, "", "Invalid value for $iTimestampType")
    
    
    ; führt immer zu Fehler, wenn $iTimestampType <> 0
    
    
    If $iTimestampType < 0 Or $iTimestampType > 2 Then Return SetError(5, "", "Invalid value for $iTimestampType")


    @Zeitriss
    Deine Idee finde ich super. Die Funktion hat sich aus einem Fallbeispiel entwickelt, das @Bitnugger sicher auch vor Augen hatte. Mit frei bestimmbaren Zeiten (und _NowCalc() vllt. als Standardwert) ist es aber definitv besser.
    EDIT: Auch cool, dass du _FileListToArrayRec ersetzt hast. Direkt nur aufzunehmen, was hineingehört ist natürlich viel besser.

    Grüße autoiter

    Einmal editiert, zuletzt von autoiter (7. Februar 2017 um 20:09)

  • @Zeitriss

    FileGetTime() - ja, stimmt... und wieder ein Fehler weniger!

    DllCall() vs StringRegExpReplace() - ja, stimmt... doch mit StringRegExp() geht es sogar noch einen Tacken schneller!

    Beim gesamten Konzept frage ich mich ob eine Funktion, welche Dateien auf einem frei bestimmbaren Zeitraum überprüft nicht deutlich sinnvoller wäre.

    Ja klar... war auch mein erster Gedanke, als ich das Script von @autoiter sah, dachte ich... oh, super, da hat mir einer die Arbeit abgenommen... aber so ganz dann doch nicht. An meiner Funktion bin ich noch am stricken... dauert noch etwas, bis ich sie fertig habe, was ich hier...

    Hihi... nein, nicht nur kürzen... und jetzt nicht lachen, aber ich war auch dabei mir so eine Funktion zu tippern... und dann sehe ich dann dein Script hier.

    aber auch schon angedeutet hatte.

    Habe schnell mal hierzu auch mal was zusammen gebastelt, ist allerdings "quick and dirty".

    Grins... "quick and dirty" - so mag ich es beim Sex... doch was meine Scripte angeht, ist es ein absolutes Tabu... :rofl:

    _FileListBetweenDates() - oh ja, und auch wieder eine gute Idee!

    Hm... warum so kompliziert?

    If Not (0 <= $iTimeStep and $iTimeStep <= 2) Then Return SetError(3)

    If $iTimeStep < 0 Or $iTimeStep > 2 Then Return SetError(3)

    Ich werde mir dein "quick and dirty" aber auch noch mal genauer anschauen...


    @autoiter

    mittlerweile habe ich deine Variante mal genauer angeschaut. Viel cooles Zeug für mich dabei!

    Schön... das freut mich sehr... ;)

    StringFormat finde ich sehr schön. Ich benutze das viel zu selten ...

    Das ging mir lange Zeit auch so... bis ich dann den Gedankenblitz bekam, dass sich viele Dinge dramatisch optimieren lassen, wenn man sich die Beschreibungen/Kommentare zu den Funktionen genauer anschaut und diese im Hinterkopf behält!!!

    Die DLL-Variante in der Funktion __ListFilesByAge_DeletePaths($aReturnedFiles) finde ich interessant. Ich habe es mehrfach mit 10.000 Dateien getestet. Die DLL-Variante ist immer etwas schneller gewesen. In dem Fall zwar nicht die Welt, aber dennoch zu bevorzugen. Wie bist du darauf (shlwapi.dll) gekommen?

    Durch die Funktion _WinAPI_PathStripPath() ; #include <WinAPIShPath.au3>
    Meine Variante...

    AutoIt
    $aReturnedFiles[$i][0] = DllCall($hDll, 'none', 'PathStripPathW', 'wstr', $aReturnedFiles[$i][0])[1]

    ist quasi nur eine verkürzte Form davon... weil in unserem Script ja garantiert ist, dass ein Pfad vorhanden ist, der entfernt werden soll.

    Am geilsten finde ich aber die Geschichte mit den $iAgeAndOlder Fällen!

    Darauf gekommen bin ich durch die Funktion FuncName() - womit dann klar war, dass du Funktionsnamen in eine Variable packen kannst... und die Funktion dann mit dieser Variable aufrufen kannst. Der erste User, bei dem ich gesehen habe, das er das auch kennt, war @4ern.

    Streng genommen lässt das noch nicht abgefangene "ym" oder so einen Quatsch zu, ...

    AutoIt
    If StringReplace("dymwhns", $sUnit, '') And @extended <> 1 Then Return SetError(4, "", "Invalid unit")

    Hier hast du einen falschen Gedankengang... StringReplace() liefert in @extended die Anzahl der Ersetzungen... und damit das als Einzeiler funktioniert, mache dann einfach ein If...Then draus... es darf also nur genau 1 Zeichen ersetzt worden sein!

    Bei den Bedingungen gibt es noch einen Fehler.

    Oh ja, böder Fehler... <>1... den ich aber schon repariert habe.

    Die Funktion hat sich aus einem Fallbeispiel entwickelt, das @Bitnugger sicher auch vor Augen hatte.

    Jeep... stimmt genau!

    Meine Funktion hat momentan folgende Parameter...

    AutoIt
    Func _FileListToArrayRecExt($sFilePath, $sMask = '*', $iReturn = $FLTAR_FILES, $iRecur = $FLTAR_NORECUR, $iSort = $FLTAR_NOSORT, $iReturnPath = $FLTAR_RELPATH, $iDateFlags = $FLTAR_ANY, $sStartDate = '*', $sEndDate = '*', $iDateMode = $FT_MODIFIED)

    5 Mal editiert, zuletzt von Bitnugger (9. Februar 2017 um 05:56)

  • Wie kann ich dieses Format ändern:
    C:\Users\***\Desktop\Sia - Never Give Up.mp3|2017/01/15 11:26

    also vorallem das mit der pipe in:

    2017/01/15 11:26 - C:\Users\***\Desktop\Sia - Never Give Up.mp3


    hoffe da bin ich hier jetzt richtig

    neben AutoIt jetzt auch noch in C/C++, Java und Python aktiv :)
    Stand 04.04.2018, 13:34

  • Wie kann ich dieses Format ändern:
    C:\Users\***\Desktop\Sia - Never Give Up.mp3|2017/01/15 11:26

    Zuerst - nein, hier bist du nicht richtig... denn du hast einen fremden Thread gekapert, der mit deiner Frage nichts zu tun hat.

    Hier geht es um das Thema "Dateien nach Alter/Bearbeitungszeit/Zugriffszeit auflisten."

    Du hättest einen eigenen Thread eröffnen müssen... dazu klickst du einfach auf den Button "Neues Thema".

    Zu deiner Frage... das kannst du z. B. so machen:

    AutoIt
    Local $sString  = 'C:\Users\***\Desktop\Sia - Never Give Up.mp3|2017/01/15 11:26'
    Local $sConvert = StringRegExpReplace($sString, '(.+)\|(.+)', '$2 - $1')
    ConsoleWrite('$sString  = ' & $sString & @CRLF)
    ConsoleWrite('$sConvert = ' & $sConvert & @CRLF)
  • ; $iTimestampType - defines which timestamp is used.
    ; 0 = creation time
    ; 1 = last modification time
    ; 2 = last access time

    Hier hattest du dich übrigens auch vertan...

    Richig ist:

    $FT_MODIFIED (0) = Last modified (default)
    $FT_CREATED (1) = Created
    $FT_ACCESSED (2) = Last accessed

  • Hi @Bitnugger,

    nein, ich hatte das in der Funktion einfach anders umgesetzt. Ich nehme mal an, du hast jetzt nicht nochmal in meinen Code geschaut, sondern schon in deinen gekürzten. Eigentlich ging es ja um das Fallbeispiel, wo jemand wissen wollte welche Dateien sind älter als 14 Tage. Das wollte ich auch als Standardwert und fand es auch schöner wenn ich das nicht nur zum Standardwert mache, sondern es auch als erstes in der Liste steht - reiner Spleen. Dafür war das folgende. Ich hatte die Konstanten benutzt, damit man später noch nachvollziehen kann, was der Quatsch soll ^^

    AutoIt
    If $iTimestampType = 0 Then
    		$iTimestampType = $FT_CREATED
    	ElseIf $iTimestampType = 1 Then
    		$iTimestampType = $FT_MODIFIED
    	ElseIf $iTimestampType = 2 Then
    		$iTimestampType = $FT_ACCESSED
    	EndIf

    Grüße autoiter

  • So in etwa habe ich mir das vorgestellt...

    _FileListToArrayRecExt()

    2 Mal editiert, zuletzt von Bitnugger (9. Februar 2017 um 13:28)

  • Hey @Bitnugger,
    die Parameter betrachtend, würde ich sagen, da bleiben keine Wünsche offen, wenn du fertig bist :D

    PS: Nur ein Vorschlag für _FileListToArrayRecExt. Wahrscheinlich ist es sauberer, wenn du das ursprüngliche Ausgeben einer Textdatei streichst und wirklich nur die Dateien in ein Array listest (so wie es in den anderen _FileListToArray-Funktionen auch ist).

    Grüße autoiter

  • Es ist natürlich ein Ansatz für jeden speziellen Suchfall eine spezielle Funktion zu schreiben.
    Eine Alternative hierzu wäre es wenn man die Dateisuche einmal komplett schreibt und die speziellen Suchkriterien derart flexibel gestaltet, dass der Nutzer den speziellen Teil selbst schreiben/austauschen kann.

    Hier mal als Beispiel wie ich das meine:

    Die Funktion ist so aufgebaut, dass eine benutzerdefinierte Funktion als Parameter übergeben werden kann welche dann jeweils alle gefundenen Dateien ganz nutzerspezifisch prüft.

  • Irre gutes Design @AspirinJunkie,
    das funktioniert hervorragend und ist super schnell angepasst. Sehr schönes Anschauungsmaterial. Das muss ich mir auf jeden Fall merken. Danke dir!
    (Auch schon wieder ein Funktionsaufruf in einer Variable. So etwas ist mir ewig nie aufgefallen :D ).

    @Bitnugger ändere einfach mal die Callback-Funktion. Vielleicht hast du da einfach keine Datei jünger als 14 Tage? Such vllt. mal Stringinstring ".dll"

    Grüße autoiter

  • doch leider funktioniert dein Beispiel nicht.

    Nachdem ich "#include <Array.au3>" hinzugefügt hatte, lief es zwar, doch das Ergebnis-Array ist leer!

    Das vergessene Array.au3 hatte ich vergessen und später hinzugefügt.
    Bei dem leeren Array vermute ich, wie autoiter, dass keine Datei den Anforderungen entspricht.
    Einfach mal z.B. den Zeitraum auf z.B. 100 Tage verlängern.
    Hier auch mal mit der rekursiven Schwesterfunktion dazu:

    Auch schon wieder ein Funktionsaufruf in einer Variable. So etwas ist mir ewig nie aufgefallen

    Seit es das Feature gibt (kam erst relativ spät) nutze ich es exzessiv.
    Damit wird deutlich dynamischerer Code ermöglicht und daher gibt es eine Vielzahl von Einsatzzwecken hierfür.
    Schau z.B. mal in die DynArray-UDF - die ist voll von Anwendungsfällen für Funktionsvariablen.
    Auch für mathematische Kurvendiskussion lässt sich dies ziemlich sinnvoll einsetzen.

  • @Bitnugger ändere einfach mal die Callback-Funktion. Vielleicht hast du da einfach keine Datei jünger als 14 Tage? Such vllt. mal Stringinstring ".dll"

    Ja danke - stimmt... bin ich vorhin auch selbst drauf gekommen... habe die Funktion cb_MyFunc ein wenig umgestrickt, damit man $iDiff und $s_Path sehen kann.


    AutoIt
    ; Callback-Funktion welche True zurückgibt, wenn eine Datei den Anforderungen entspricht und False wenn nicht
    ; Konkrete Prüfung hier: Test ob Datei jünger als 100 Tage ist:
    Func cb_MyFunc(Const $s_Path)
    	Local Static $Now = _NowCalc(), $iDiff, $sColor, $iMin = 100
    	$iDiff = _DateDiff("D", StringRegExpReplace(FileGetTime($s_Path, 0, 1), "(\d{4})(\d{2})(\d{2}).+", "$1/$2/$3"), $Now)
    	$sColor = ($iDiff < $iMin) ? '+' : '!'
    	ConsoleWrite(StringFormat('%s $iDiff = %6i \t %s\r\n', $sColor, $iDiff, _WinAPI_ExpandEnvironmentStrings($s_Path)))
    	Return ($iDiff < $iMin) ? True : False
    EndFunc   ;==>cb_MyFunc

    Doch was ich noch nicht verstehe, warum im erstes Ergebnis die Umgebungsvariable nicht expandiert wurde...
    ! 147 C:\Windows\System32\%TMP%

    TMP.png

    Habe dann die Zeile geändert... dann passt es.

    AutoIt
    ;Local $aRet = StringSplit(StringTrimRight($sRet, 1), "|", $flgSS)
    Local $aRet = StringSplit(StringTrimRight(_WinAPI_ExpandEnvironmentStrings($sRet), 1), "|", $flgSS)