pretznbaer
Goto Top

Ordner und Dateinamen mit dazugehöriger Besitzerinformation auslesen und in csv Datei ausgeben

Hallo,

wie in einem anderen Beitrag von mir Besitzer von Ordnern UND Dateien mit vbs in excel ausgeben schon einmal erwähnt bzw. beschrieben, versuche ich mittels Batch Datei, aus einem Verzeichnis folgende Informationen auszulesen:
  • Ordnernamen
  • Dateinamen
  • dazugehörigen Besitzerinformation

Kurz und Bündig: Lokal funktioniert es einwandfrei aber auf Windows Server 2003 nicht...

Bisher lief die "Anwendung" (--> Siehe Code unten) auf meiner lokalen Maschine (win XP) und ist daher etwas in Vergessenheit geraten. Und da dies jetzt wieder aktuell geworden ist, bin ich auf ein weiteres Problem gestoßen:

Derzeit liest die Anwendung nur lokal ein Gesamtes Verzeichnis mit den zusätzlichen Besitzerinformationen aus und gibt die Informationen in einer .csv Liste aus.
Nun wollte ich dieses auf Windows Server (2003 [Englisch]) ausführen und ein Verzeichnis auf dem Server auslesen, aber es wird, im warsten Sinne des Wortes, irgendwas ausgegeben.

Hier der Code
@echo off & setlocal
set "Ordner=D:\"  
set "Liste=D:\%date:~-4% %date:~-7,2% %date:~-10,2% liste.csv"  
del "%Liste%" 2>nul   

for /r "%Ordner%" %%i in (.) do for /f "tokens=1,2,3,4,*" %%a in ('dir "%%i" /q ^|findstr "^[0-9].*"^|findstr /v ".*DIR.*\."') do echo %%a;%%b;%%c;%%d;%%~fi\%%e;>>"%Liste%"  

Ziel ist, dass die selben Informationen auch ausgegeben werden, wenn die "Anwendung" auf Win Server 2003 ausgeführt wird. Ich persönlich glaube, dass es an der Englischen Version liegt, weiß jedoch nicht, wie ich dies lösen könnte.

Ich hoffe, dass ich alles soweit verständlich beschrieben habe?! Solltet ihr weitere Infos benötigen, gebt mir einfach bescheid face-smile

Freue mich über jeden Lösungsvorschlag,

mfg
Pretznbaer

Content-ID: 202205

Url: https://administrator.de/contentid/202205

Ausgedruckt am: 29.11.2024 um 14:11 Uhr

Friemler
Friemler 22.02.2013, aktualisiert am 23.02.2013 um 23:37:12 Uhr
Goto Top
Hallo Pretznbaer,

in Deinem Schnipsel müsste der Suchstring des zweiten FINDSTR-Befehls statt
".*DIR.*\."
so
".*DIR.*\.$"
lauten, zumindest auf Windows 7.


Es gibt allerdings weiterhin zwei Probleme mit dem Code:

  1. Falls als Besitzer bei einer Datei/einem Verzeichnis z.B. die Benutzergruppe Vordefiniert\Administratoren eingetragen ist, wird dieser Name bei der Darstellung durch den DIR-Befehl wegen Überlänge abgeschnitten und zusätzlich mit dem Dateinamen verschmolzen, da kein Freizeichen dazwischen geschrieben wird. Dadurch entsteht eine fehlerhafte Zeile in der CSV-Datei.
  2. Die Extraktion der einzelnen Bestandteile des Datums zur Erzeugung des Namens der Ausgabedatei wird bei einem englischen bzw. amerikanischen Windows so nicht funktionieren, da in beiden Lokalisierungen ein anderes Datumsformat benutzt wird, d.h. die Position von Tag, Monat und Jahr in einem englischen/amerikanischen Datumsstring stimmen nicht mit den Positionen in einem deutschen Datumsstring überein.

Als Lösung bietet sich VBScript an. Mit einem kleinen Inline-VBScript könnte man die Datumszerlegung so erledigen, dass sie auf allen Sprachversionen von Windows funktioniert und immer das gleiche Ergebnis liefert.

Für die Ermittlung des Besitzers einer Datei/eines Verzeichnisses bei der Erzeugung der CSV-Datei existiert eine sichere aber recht langsame Lösung über WMI.

Wie viele Dateien/Verzeichnisse sind (ungefähr) in dem zu verarbeitenden Verzeichnisbaum?

Gruß
Friemler
Pretznbaer
Pretznbaer 25.02.2013 um 07:41:21 Uhr
Goto Top
Hallo Friemler,

vielen Dank für deine Antwort! Konnte diese leider erst jetzt lesen.

zu 1.) sofern ich das richtig verstanden habe, könnte es daran scheitern, dass die Benutzergruppen "zu lang" sind? Meine Frage dazu: was haben die "Benutzergruppen" mit der "Besitzerinformation" zu tun? Oder verstehe ich hier etwas falsch?

zu 2.) Dies hat aber keine direkte Auswirkung auf die ausgelesenen Werte oder?

Sorry, aber bin was Programmierung angeht noch nicht besonders weit "entwickelt" :D

Die Anzahl an Dateien/Ordner im Verzeichnisbaum ist von Tag zu Tag verschieden. Da auf diesen Ordner/Verzeichnis ca. 20 Personen Zugriff haben, möchte ich die "An und Abgänge" von Dateien/Ordnern pro Tag überwachen bzw. dokumentieren. Im Schnitt wären das ca. 15 Ordner mit ungefähr 150 Dateien. (Könnte aber auch manchmal doppelt so viel sein)

Mit VBScript hatte ich es auch schon versucht, bin aber auch an der selben Stelle wie mit dem Batch-file gescheitert...

Lg
Pretznbaer
Friemler
Friemler 25.02.2013 aktualisiert um 20:23:03 Uhr
Goto Top
Hallo Pretznbaer,

beim Durchforsten meiner Festplatte mit Deinem Batchschnipsel sind Dateien aufgetaucht, deren Besitzer nicht ein Benutzer sondern z.B. die Benutzergruppe Administratoren ist, deren vollständiger Name auf einem deutschen Windows Vordefiniert\Administratoren lautet. Da DIR /Q die Daten zu einer Datei in einer Tabellenstruktur mit fester Spaltenbreite anzeigt, blieb davon nur Vordefiniert\Administra übrig. Direkt neben der Spalte für den Besitzer wird ja der Dateiname angezeigt, in diesem Fall aber ohne separierendes Leerzeichen. Die FOR-Schleife hat als Besitzer somit Vordefiniert\AdministraXXXXXXXX ermittelt (XXXXX steht für den Teil des Dateinamens bis zum ersten Freizeichen). => Die CSV-Datei enthielt einen falschen Eintrag.

Wenn es nur 150 Dateien sind, spielt die Ausführungsgeschwindigkeit keine große Rolle, da sollte die robuste WMI-Lösung genügen.

Aber erstmal der Code für das Batchfile, von dem aus das VBScript aufgerufen wird, das die eigentliche Arbeit erledigt:
@echo off & setlocal

::Das Verzeichnis, in dem das Script gespeichert ist, zum aktuellen Verzeichnis machen
pushd "%~dp0"  

::Aktuelles Datum bestimmen und in die Variable DateStamp schreiben
call :GetDate DateStamp


::Basisverzeichnis
set "Ordner=D:\"  

::Zieldatei
set "Liste=D:\%DateStamp% Liste.csv"  

::Pfad+Name des VBScripts, das die Datei- und Verzeichnisdaten ermittelt
set "VBSGetTreeInfo=GetTreeInfo.vbs"  


::Evtl. bereits existierende Zieldatei löschen
del "%Liste%" 2>NUL  

::VBScript ausführen, dabei das Basisverzeichnis als Parameter übergeben
::Die Ausgabe des VBScripts in die Zieldatei umleiten
cscript /nologo "%VBSGetTreeInfo%" "%Ordner%" > "%Liste%"  

::Das Verzeichnis, das beim Scriptstart das aktuelle Verzeichnis war,
::wieder zum aktuellen Verzeichnis machen
popd

::Script Ende
exit /b



::Unterprogramm, das mit Hilfe eines temporären VBScripts
::das aktuelle Datum bestimmt und im Format DD-MM-YYYY zurückgibt
:GetDate
  ::Pfad für temporäres Script
  set "VBSGetDate=%TEMP%\GetDate.vbs"  
  
  ::Temporäres VBScript schreiben
  > "%VBSGetDate%" echo dtToday = Date()  
  >>"%VBSGetDate%" echo WScript.Echo Year(dtToday) ^& "-" ^& Right("0" ^& Month(dtToday), 2) ^& "-" ^& Right("0" ^& Day(dtToday), 2)  
  
  ::Temporäres VBScript ausführen und das Ergebnis in der Variablen speichern,
  ::deren Name als 1. Parameter übergeben wurde
  for /f "delims=" %%d in ('cscript /nologo "%VBSGetDate%"') do (  
    set "%~1=%%d"  
  )
  
  ::Temporäres VBScript löschen
  del "%VBSGetDate%"  

::Rücksprung
exit /b

In Zeile 17 des Scripts wird der Name und Pfad des VBScripts gesetzt, das die Daten der Dateien und Verzeichnisse im zu verarbeitenden Verzeichnisbaum ermittelt. Hier wird der Name GetTreeInfo.vbs verwendet. Das folgende Script muss unter diesem Namen gespeichert werden:
Option Explicit

Const typeFolder = 0
Const typeFile   = 1

Dim objFSO, objWMIService, intRetVal

Set objFSO        = CreateObject("Scripting.FileSystemObject")  
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\root\cimv2")  

If WScript.Arguments.Count > 0 Then
  If objFSO.FolderExists(WScript.Arguments(0)) Then
    TraverseFolderTree objFSO.GetFolder(WScript.Arguments(0))
    intRetVal = 0
  Else
    WScript.StdErr.WriteLine "Fehler: Verzeichnis wurde nicht gefunden."  
    intRetVal = 2
  End If
Else
  WScript.StdErr.WriteLine "Fehler: Bitte geben Sie ein Verzeichnis an."  
  intRetVal = 1
End If

WScript.Quit intRetVal



Sub TraverseFolderTree(objFolder)
  Dim colSubFolders, objSubFolder, colFiles, objFile
  
  Set colSubFolders = objFolder.SubFolders
  Set colFiles      = objFolder.Files

  For Each objSubFolder In colSubFolders
    WScript.StdOut.WriteLine GetItemInfo(typeFolder, objSubFolder)
  Next
  
  For Each objFile In colFiles
    WScript.StdOut.WriteLine GetItemInfo(typeFile, objFile)
  Next
  
  For Each objSubFolder In colSubFolders
    TraverseFolderTree objSubFolder
  Next
End Sub


Function GetItemInfo(intType, objItem)
  Dim arrItemInfo(4), dtTimeStamp
  
  dtTimeStamp = objItem.DateLastModified
  
  arrItemInfo(0) = Right("0" & Day(dtTimeStamp), 2) & "." & Right("0" & Month(dtTimeStamp), 2) & "." & Year(dtTimeStamp)  
  arrItemInfo(1) = Right("0" & Hour(dtTimeStamp), 2) & ":" & Right("0" & Minute(dtTimeStamp), 2)  
  
  Select Case intType
    Case typeFolder arrItemInfo(2) = "<DIR>"  
    Case typeFile   arrItemInfo(2) = objItem.Size
  End Select  

  arrItemInfo(3) = GetOwner(objItem.Path)
  arrItemInfo(4) = objItem.Path
  
  GetItemInfo = Join(arrItemInfo, ";")  
End Function


Function GetOwner(strItemName)
  Dim objFileSecuritySettings, objSecurityDescriptor
  
  strItemName = Replace(strItemName, "\", "\\")  

  Set objFileSecuritySettings = objWMIService.Get("Win32_LogicalFileSecuritySetting=""" & strItemName & """")  
  objFileSecuritySettings.GetSecurityDescriptor objSecurityDescriptor

  GetOwner = objSecurityDescriptor.Owner.Domain & "\" & objSecurityDescriptor.Owner.Name  
End Function

Um komplett zu sein: Hier noch die etwas unsichere aber viel schnellere Alternative über das Shell COM-Objekt. Diese Version berücksichtigt keine versteckten Dateien. Außerdem muss verhindert werden, dass ZIP- oder andere Archive als Verzeichnisse angesehen werden, deshalb die etwas aufwendigen Abfragen in den Zeilen 33, 39 und 45.
Option Explicit

Const typeFolder = 0
Const typeFile   = 1

Dim objFSO, objShell, intRetVal

Set objFSO   = CreateObject("Scripting.FileSystemObject")  
Set objShell = CreateObject("Shell.Application")  

If WScript.Arguments.Count > 0 Then
  If objFSO.FolderExists(WScript.Arguments(0)) Then
    TraverseFolderTree objShell.NameSpace(WScript.Arguments(0))
    intRetVal = 0
  Else
    WScript.StdErr.WriteLine "Fehler: Verzeichnis wurde nicht gefunden."  
    intRetVal = 2
  End If
Else
  WScript.StdErr.WriteLine "Fehler: Bitte geben Sie ein Verzeichnis an."  
  intRetVal = 1
End If

WScript.Quit intRetVal




Sub TraverseFolderTree(objFolder)
  Dim objItem

  For Each objItem In objFolder.Items
    If objItem.IsFolder And objItem.IsFileSystem And (objItem.Size = 0) Then
      WScript.StdOut.WriteLine GetItemInfo(typeFolder, objItem)
    End If
  Next
  
  For Each objItem In objFolder.Items
    If ((Not objItem.IsFolder) And objItem.IsFileSystem) Or (objItem.IsFolder And objItem.IsFileSystem And (objItem.Size <> 0)) Then
      WScript.StdOut.WriteLine GetItemInfo(typeFile, objItem)
    End If
  Next
  
  For Each objItem In objFolder.Items
    If objItem.IsFolder And objItem.IsFileSystem And (objItem.Size = 0) Then
      TraverseFolderTree objItem.GetFolder
    End If
  Next
End Sub


Function GetItemInfo(intType, objItem)
  Dim arrItemInfos(4), dtTimeStamp

  dtTimeStamp = objItem.ModifyDate
  
  arrItemInfos(0) = Right("0" & Day(dtTimeStamp), 2) & "." & Right("0" & Month(dtTimeStamp), 2) & "." & Year(dtTimeStamp)  
  arrItemInfos(1) = Right("0" & Hour(dtTimeStamp), 2) & ":" & Right("0" & Minute(dtTimeStamp), 2)  
  
  Select Case intType
    Case typeFolder arrItemInfos(2) = "<DIR>"  
    Case typeFile   arrItemInfos(2) = objItem.Size
  End Select

  arrItemInfos(3) = objItem.ExtendedProperty("Owner")  
  arrItemInfos(4) = objItem.Path
  
  GetItemInfo = Join(arrItemInfos, ";")  
End Function

Gruß
Friemler
Pretznbaer
Pretznbaer 04.03.2013 um 15:49:24 Uhr
Goto Top
Hallo Friemler,

vielen Dank für deine Hilfe.
Habs mit deiner vorgeschlagenen Lösung ausprobiert und hat auch funktioniert, jedoch hat sich meine Frage so zu sagen von selbst gelöst.

Mein "Schnipsel" funktioniert einwandfrei, egal ob deutsches oder englisches OS.
Problem war nur:

Ich habe vergessen zu erwähnen, dass das Verzeichnis welches ich auslesen möchte, sich in einer anderen Domäne als der "tatsächliche" Rechner (der das Programm ausführt) befindet und somit nicht die BERECHTIGUNGEN für das Auslesen aus dem Active Directory der anderen Domäne vorhanden waren. Das Laufwerk wird zwar auf die andere Domäne gemappt jedoch sind keine Berechtigungen etc. vorhanden.

Lösung daher: ausführen des Programms auf einem Rechner, der sich in der selben Domäne wie das Zielverzeichnis befindet...


Vielen lieben Dank trotzdem für deine Hilfe face-smile

Lg
Pretznbaer