Tag Archive for 'Attribute'

Der Tellerrand: Directory Scanner in Purebasic

Die Aufgabe ist eigentlich relativ simpel: Man schreibe einen Directory Scanner, der einfach ab einem bestimmten Verzeichnis die Namen,Pfade, Zeitstempel und Attribute aller Dateien und Subdirectories ausliest und in eine CSV Datei schreibt.

So einfach wie sich das anhört ist es zuerst auch umgesetzt: Eine rekursive Funktion dazu ist schnell geschrieben, und in der eigenen Codebase (oder auch hier bei dotnet-snippets.de) findet sich auch eine Klasse um CSV Dateien simpel zu schreiben. Tests auf der lokalen Maschine verlaufen auch erfolgreich. Alles ok.

Nun kommt aber die harte Realität: Läßt man das ganze nun mal zum Beispiel über einen PSDP (Primary Software Distribution Point) laufen, der circa 1 Million Dateien und Verzeichnisse enthält laufen wird man schnell feststellen, das die Performance der ganzen Sache mit .Net nun nicht grade doll ist.

Vor einiger Zeit hatte ich mich schon einmal mit Purebasic beschäftigt. Das ist eine prozedurale an Basic angelehnte Programmiersprache. Das interessante daran ist allerdings, das der Compiler richtig schnellen nativen 32bit Code produziert. Noch dazu ist es plattformunabhängig, also auch zum Beispiel für Linux verfügbar.
Also wird die ganze Aufgabe nocheinmal mit Purebasic implementiert. Wer sich nun gar nicht vorstellen kann wie das ganze dann aussieht, hier der Quellcode:

Structure fileinfo
  name.s
  path.s
  size.s
  created.s
  modified.s
  comment.s
EndStructure
Global NewList filelist.fileinfo()
Global scandate$
Global pumptofileparam.w

Procedure WriteList(scancomplete$)
  ResetList(filelist())
  While NextElement(filelist())
    If scancomplete$ = "True"
      If ListIndex(filelist()) = CountList(filelist())-1
        WriteString(0,filelist()\name + ";" + filelist()\path + ";" + filelist()\modified +";"+filelist()\created+";"+scandate$+";"+filelist()\size + ";" + filelist()\comment)
      Else
        WriteStringN(0,filelist()\name + ";" + filelist()\path + ";" + filelist()\modified +";"+filelist()\created+";"+scandate$+";"+filelist()\size + ";" + filelist()\comment)
      EndIf
    Else
      WriteStringN(0,filelist()\name + ";" + filelist()\path + ";" + filelist()\modified +";"+filelist()\created+";"+scandate$+";"+filelist()\size + ";" + filelist()\comment)
    EndIf

  Wend
EndProcedure

Procedure ScanDir(Directory$)
  Dir.l = ExamineDirectory(#PB_Any, Directory$, "*.*")
  If Dir
    While NextDirectoryEntry(Dir)
      name$ = DirectoryEntryName(Dir)
      If name$ <> "."
        If name$ <> ".."
          AddElement(filelist())
          filelist()\name = name$
          filelist()\path = Directory$ + "\"
          filelist()\size = Str(DirectoryEntrySize(Dir))
          filelist()\created = FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", DirectoryEntryDate(Dir,#PB_Date_Created))
          filelist()\modified = FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss",DirectoryEntryDate(Dir,#PB_Date_Modified))
          filelist()\comment = Str(DirectoryEntryAttributes(Dir))
          If CountList(filelist()) = pumptofileparam
            WriteList("False")
            ClearList(filelist())
          EndIf
          If DirectoryEntryType(Dir) = #PB_DirectoryEntry_Directory
            ScanDir(Directory$ + "\" + name$)
          EndIf
        EndIf
      EndIf
    Wend
    FinishDirectory(Dir)
  EndIf
EndProcedure

OpenConsole()
If CountProgramParameters() => 2
  CreateFile(0,ProgramParameter(1))
  scandate$ = FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss",Date())
  pumptofileparam = Val(ProgramParameter(2))
  If pumptofileparam = 0
    pumptofileparam = 5000
  EndIf
  ScanDir(ProgramParameter(0))
  WriteList("True")
  CloseFile(0)
Else
  PrintN("Usage:")
  PrintN("scan.exe [Share to scan] [Output csv file]")
  PrintN("")
  PrintN("Fieldterminator: ;")
  PrintN("Rowterminator: \r")
EndIf

Die Laufzeit des Scannens beträgt hier nun nur noch knapp 8 Minuten, nicht schlecht für circa 1 Million Dateien. Und es wird eine circa 150MB große CSV generiert, das man z.B. mittels Bulk Insert in eine Datenbank importierten könnte und eine History erzeugen könnte.

Fazit: Manchmal hilft ein Blick über den Tellerrand doch mal um Probleme kreativ zu lösen.