Verzeichnistiefe vorgeben

fahrrad
Goto Top

Hallo zusammen,

ich habe da ein kleines Problem bzw. eine kleine Herausforderung

ich möchte unter Windows 2003 in einer Batch die maximale Verzeichnistiefe vorgeben können, welche dann per Dir ausgegen wird,

zB mit Tiefe=3 und

C:\test1\test2\test3\test4\test5 nur noch C:\test1\test2\test3 ausgebenb wird oder

Tiefe=5

P:\ich\du\er\sie\es\wir\ihr\sie nur noch P:\ich\du\er\sie\es\

die Namen der einzelnen Verzeichnisse sind völlig willkürlich.

Letztenendes möchte ich ein Dir über ein Netzlaufwerk machen und eben nur bis zu einer vorgebbaren Tiefe rekursiv die Verzeichnisstruktur ermitteln.

Vielen Dank für Eure Bemühungen

Gruß
Fahrrad

Content-Key: 148442

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

Ausgedruckt am: 04.07.2022 um 11:07 Uhr

Mitglied: 60730
60730 05.08.2010 um 16:38:36 Uhr
Goto Top
Moin

schau dir mal

  • for /?
an
dann schaust du auf deine "Tiefe" und merkst - das Zeichen \ gibts nur dann, wenn du einen Sprung tiefer gehst.
Ergo ist deine Grenze Token das Zeichen \
  • Set /p
damit kannst du parameter füllen und die verwerten.

Gruß
Mitglied: Friemler
Friemler 05.08.2010 um 21:21:38 Uhr
Goto Top
Hallo Fahrrad,

hier mein Vorschlag zur Lösung des Problems:

@timobeil: So einfach, wie Du es in Deinem Posting darstellst, ist die Sache nicht. Der Knackpunkt ist, das in der FOR-Schleife eine unbekannte Anzahl von Laufvariablen entsteht, je nach Tiefe des gerade verarbeiteten Pfades. Der Code müsste also jedes mal angepasst werden.



back-to-topAufruf
Wenn Du den Code als RDir.bat abspeicherst, wird das Skript so aufgerufen:

rdir <Verzeichnis> <Tiefe>

Verzeichnis ist das Basisverzeichnis, von dem ausgegangen wird. Enthält der Pfad Leerzeichen, muss Verzeichnis in Anführungszeichen eingeschlossen werden.

Tiefe ist deine Verzeichnistiefe, wobei ein Wert von 1 nur das Basisverzeichnis listet


back-to-topEinschränkungen
Für das Basisverzeichnis muss immer ein vollständiger Pfad mit Laufwerksbuchstaben angegeben werden,
z.B. E:\Ordner1\Ordner2.

Die Verzeichnisbäume, die mit dem Skript verarbeitet werden, dürfen kein Verzeichnis enthalten, in dessen Name eines der Zeichen ^&! vorkommt, sonst kommt es zu Fehlfunktionen. Vielleicht hat noch jemand eine Idee dazu.

Verzeichnisse mit gesetztem System-Attribut werden nicht verarbeitet, das führt meist sowieso nur zu Fehlermeldungen weil die Zugriffsrechte fehlen.


back-to-topErklärung
In Zeile 6 wird geprüft, ob das letzte Zeichen von Verzeichnis ein Backslash ist und dieser ggf. entfernt.

Zeile 8 ruft eine Funktion auf, die zählt, wie oft im ersten Parameter das Zeichen im zweiten Parameter (hier also \) vorkommt. Das Ergebnis wird um die übergebene Verzeichnistiefe erhöht. Die Tilde (~) bei %~2 sorgt dafür, daß, falls Tiefe in Anführungszeichen eingeschlossen wurde, diese entfernt werden. Somit kann man nicht nur Wurzelverzeichnisse als Basisverzeichnis übergeben, sondern auch ein Unterverzeichnis auf einer beliebigen Ebene.

In Zeile 11 wird das Unterprogramm RDir (die eigentliche Programmlogik) aufgerufen. Parameter sind (das bereinigte) Verzeichnis und Tiefe.

In Zeile 14 wird das Skript nach Rückkehr aus dem Unterprogramm beendet.


back-to-topUnterprogramm RDir
In Zeile 19 wird die verzögerte Variablenerweiterung eingeschaltet (wird in der FOR-Schleife gebraucht, deshalb steht dort !newdir! statt %newdir%).

In Zeile 23 wird geprüft, ob das letzte Zeichen von actdir ein Doppelpunkt ist. Durch Zeile 6 wurde ja ein Backslash als letztes Zeichen des Basisverzeichnisses entfernt. actdir würde also das Wurzelverzeichnis eines Laufwerks darstellen. Wenn dem so ist, wird die Hilfsvariable actdir2 in Zeile 24 mit dem Inhalt von actdir plus einem Backslash belegt. Wenn nicht, wird actdir2 in Zeile 26 nur mit dem Inhalt von actdir belegt. Dieses Vorgehen vermeidet, das bei den folgenden DIR-Befehlen ein Ausdruck wie z.B. DIR E: entsteht, der das aktuell gesetzte Verzeichnis von Laufwerk E: listen würde. Andererseits darf in Zeile 34 actdir auf keinen Fall als letztes Zeichen einen Backslash enthalten, da das einen Fehler verursachen würde.

In Zeile 29 wird das übergebene Verzeichnis (für den Benutzer sichtbar) gelistet. /-p verhindert die seitenweise Ausgabe, /o:gne listet zuerst Verzeichnisse (g) und sortiert die Ausgabe zuerst nach dem Namen (n) und dann nach der Erweiterung (e). /aface-confused verhindert das Auflisten von Verzeichnissen mit gesetztem System-Attribut.

Durch die zwei folgenden echo. wird ein Zeilenvorschub erzeugt.

Im Kopf der FOR-Schleife in Zeile 33 wird das Verzeichnis nochmal gelistet (nur zur weiteren Verarbeitung in der FOR-Schleife). Durch die Option /b werden jedoch nur Namen ohne Pfad, ohne den üblichen Kopf der Ausgabe von DIR und ohne Zusammenfassung (xxx Bytes frei usw.) ausgegeben. /o:ne sorgt wieder für eine sortierte Ausgabe. Das g fehlt gegenüber dem ersten DIR-Befehl, weil durch /aface-confusedd sowieso nur Verzeichnisse gelistet werden, deren System-Attribut gelöscht ist. Durch 2^>NUL wird verhindert, das bei einem leeren Verzeichnis die Fehlermeldung "Datei nicht gefunden" ausgegeben wird.

Durch "delims=" im Kopf der FOR-Schleife wird der gesamte Inhalt einer Zeile der Ausgabe des DIR-Befehls der Laufvariablen %%d zugewiesen, pro Schleifendurchlauf eine Zeile. Ohne "delims=" würde nur der Inhalt einer Zeile bis zum ersten Leerzeichen zugewiesen.

In Zeile 34 wird somit der Name eines Verzeichnisses an das aktuelle Verzeichnis angehängt.

Durch den Aufruf von CountChr in Zeile 29 wird wieder gezählt, wie viele Backslashes der daraus resultierende Pfad enthält.

In Zeile 36 wird geprüft, ob damit die maximale Pfadtiefe erreicht ist, wenn nicht, ruft sich RDir mit dem neuen Pfad und der maximalen Pfadtiefe selbst auf. Der Ablauf beginnt somit eine Verzeichnisebene tiefer von neuem.


back-to-topFunktion CountChr
In Zeile 51 werden alle Zeichen des 1. Parameters %1 (str), die gleich dem zweiten Parameter %2 sind, gelöscht und das Ergebnis newstr zugewiesen. str behält aber seinen bisherigen Inhalt. Die Bedeutung der Tilde (~) bei %~2 wurde weiter oben schon erklärt.

In Zeile 53 wird geprüft, ob newstr ungleich str ist. Wenn str das Zeichen aus %2 enthält, ist das der Fall. Deshalb wird der Zähler nChr in Zeile 54 um eins hochgezählt.

In Zeile 56 wird geprüft, ob das letzte Zeichen von str dem Zeichen entspricht, dessen Auftreten gezählt werden soll, wenn ja wird das Unterprogramm beendet. Diese Abfrage ist nur nötig, weil der Befehl in Zeile 57 in diesem Spezialfall nicht funktioniert.

In Zeile 57 wird nämlich in str alles vor dem ersten Auftreten des Zeichens, dessen Vorkommen gezählt werden soll, inkl. dem Zeichen selbst, gelöscht und das Ergebnis wieder str zugewiesen. Dann erfolgt ein Sprung zum Anfang der Schleife, im Endeffekt also nach Zeile 51.

Durch Zeile 57 wird also schrittweise jedes Auftreten des gesuchten Zeichens eliminiert und ein neuer Durchlauf gestartet, also der Zähler nChr um 1 inkrementiert. Wenn das gesuchte Zeichen in str garnicht mehr vorkommt, sorgt Zeile 53 für einen Abbruch der Schleife. Wenn es nur noch einmal am Ende von str vorkommt, sorgt Zeile 56 für den Abbruch (vorher wurde es aber schon gezählt).

Gruß
Friemler
Mitglied: Fahrrad
Fahrrad 09.08.2010 um 08:41:48 Uhr
Goto Top
Moin Friemler,

vielen vielen Dank für Deine ausführliche Beschreibung & Hilfe!!!!!

Ich habe es ausprobiert und habe folgende Probleme

Ich brauche nur die Verzeichnisnamen, also habe ich im Skript DIR nur mit /AD /S /B aufgerufen

Er zeigt mit trotzdem ALLE Verzeichnisse an ;o(, nicjht nur bis zur Ebene, welche ich vorgegeben habe

Anbei das geänderte Skript

@echo OFF

setlocal

set "rootdir=%~1"
if "%rootdir:~-1%" equ "\" set "rootdir=%rootdir:~0,-1%"

call :CountChr "%rootdir%" \
set /a nChr+=%~2

call :RDir "%rootdir%" %nChr%

endlocal
exit /b



:RDir
setlocal enabledelayedexpansion

set "actdir=%~1"

if "%actdir:~-1%" equ ":" (
set "actdir2=%actdir%\"
) else (
set "actdir2=%actdir%"
)

dir "%actdir2%" /AD /S /B
echo.
echo.

for /f "delims=" %%d in ('dir "%actdir2%" /AD /S /B 2^>NUL') do (
set "newdir=%actdir%\%%d"
call :CountChr "!newdir!" \
if !nChr! lss %~2 call :RDir "!newdir!" %~2
)

endlocal
exit /b



:CountChr
setlocal

set "str=%~1"
set nChr=0

:CountChrLoop
call set "newstr=%%str:%~2=%%"

if "%newstr%" neq "%str%" (
set /a nChr+=1

if "%str:~-1%" neq "%~2" (
call set "str=%%str:*%~2=%%"
goto :CountChrLoop
)
)

endlocal & set "nChr=%nChr%"
exit /b

Gruß
Fahrrad
Mitglied: Friemler
Friemler 09.08.2010 um 10:04:57 Uhr
Goto Top
Hallo Fahrrad,

der Parameter /S beim DIR-Befehl bewirkt, das alle Verzeichnisse angezeigt werden. Die Verzeichnisstruktur wird dann von DIR rekursiv durchlaufen. Zeile 29 muss für Deinen Fall lauten:
Entscheidend ist das gegenüber dem Original zusätzliche d bei /aface-confusedd, dadurch werden nur Verzeichnisse gelistet.

Zeile 33 muss so bleiben, wie in meinem Original-Skript, also

In Deinen geänderten Befehlszeilen hast Du geschrieben /AD, das -s fehlt also. Dadurch werden auch Verzeichnisse mit gesetztem System-Attribut verarbeitet, je nach dem wie die Umgebungsvariable DIRCMD eingestellt ist. Das würde wahrscheinlich zu Problemen führen.

Das alles hätte Dir aber auch die Hilfe zum DIR-Befehl verraten face-wink.

Programmcode schließt man beim Posten in code-Tags ein, dann erscheinen die Zeilennummern und Einrückungen bleiben erhalten.

Gruß
Friemler
Mitglied: Fahrrad
Fahrrad 09.08.2010 um 10:48:40 Uhr
Goto Top
Hallo Friemler,

soweit funktioniert es gut. Ich habe mich aber wohl nicht so ganz klar ausgedrückt ;o((

Als Beispiel mal ein DIR /S /AD /B:

E:\Ich\Baum\Blinker
E:\Ich\Baum\Blinker\Access
E:\Ich\Baum\Blinker\Desktop
E:\Ich\Baum\Blinker\EnterpriseGuide
E:\Ich\Baum\Blinker\Eis
E:\Ich\Baum\Blinker\Eis\Dom
E:\Ich\Baum\Blinker\Eis\Dom\THW
E:\Ich\Baum\Blinker\Eis\Dom\THW\10.00
E:\Ich\Baum\Blinker\Eis\Dom\THW\10.00\Accessories

Ich brauche in meinem Ergebnis zur weiteren Verarbeitung alle Verzeichnisse bis Tiefe 5

Im beispiel also alles bis E:\Ich\Baum\Blinker\Eis\Dom, alles was danach kommt soll wegfallen, so daß nur

E:\Ich\Baum\Blinker
E:\Ich\Baum\Blinker\Access
E:\Ich\Baum\Blinker\Desktop
E:\Ich\Baum\Blinker\Enterprise
E:\Ich\Baum\Blinker\Eis
E:\Ich\Baum\Blinker\Eis\Dom
E:\Ich\Baum\Blinker\Eis\Dom
E:\Ich\Baum\Blinker\Eis\Dom
E:\Ich\Baum\Blinker\Eis\Dom

übrig bleibt.


Tja, wer lesen kann ist klar im Vorteil, ich war nur auf etwas verbeohrt.....

Gruß & Danke
Fahrrad
Mitglied: Friemler
Friemler 09.08.2010 um 10:56:11 Uhr
Goto Top
Hallo Fahrrad,

Zitat von @Fahrrad:
Tja, wer lesen kann ist klar im Vorteil

dann beherzige das mal. Um Dein gewünschtes Ergebnis zu erzielen lautet der Aufruf meines Skripts:
oder
wenn auf E: nur das Verzeichnis Ich existiert.

[Edit]
OK, um die kompletten Pfade ausgegeben zu bekommen, muss noch was geändert werden. Melde mich nochmal.

Gruß
Friemler
Mitglied: Fahrrad
Fahrrad 09.08.2010 um 11:06:48 Uhr
Goto Top
Moin Friemler,

dann erhalte ich zwar schon die "relativen" Verzeichnisse,

Blinker
Access
Desktop
Enterprise
Eis
Dom

aber nicht die absoluten

E:\Ich\Baum\Blinker
E:\Ich\Baum\Blinker\Access
E:\Ich\Baum\Blinker\Desktop
E:\Ich\Baum\Blinker\Enterprise
E:\Ich\Baum\Blinker\Eis
E:\Ich\Baum\Blinker\Eis\Dom

wobei ein DOm natürlich reichen würde

Sorry für mein dickes brett vorm Kopf

Gruß & Danke
Fahrrad
Mitglied: Friemler
Friemler 09.08.2010 um 11:17:35 Uhr
Goto Top
Hallo Fahrrad,

sorry, jetzt hatte ICH das Brett vorm Kopf.

Ersetze die Zeilen
durch

Gruß
Friemler
Mitglied: Fahrrad
Fahrrad 09.08.2010 um 11:24:01 Uhr
Goto Top
Vielen vielen Dank

Es fubnktioniert wie ich es möchte
Mitglied: Friemler
Friemler 18.08.2010 um 03:43:08 Uhr
Goto Top
@all,

ich poste hier noch eine verbesserte Version des Skripts. Es entspricht in der Funktionsweise dem Wunsch von Fahrrad, hat aber keine Einschränkungen bzgl. der Zeichen !^& und kann auch relative Pfade als Basisverzeichnis verarbeiten (also . oder .. oder P: oder Verz1\Verz2 usw.).

Die Einschränkung bzgl. der Zeichen !^& wird erstens durch die Zeilen 6, 24, 46 und 47 und zweitens durch Auslagerung der Verzeichnisausgabe und der Zeilen 35 und 36 aus dem ursprünglichen Skript in das Unterprogramm ProcessDir beseitigt (kein enabledelayedexpansion nötig, dadurch auch keine Schwierigkeiten mit dem !).

Für die Verzeichnisausgabe in Zeile 49 müssen evtl. vorhandene & Zeichen escaped werden, deshalb Zeile 46.

In einem Batch-Skript ersetzt der Skript-Interpreter beim Aufruf eines anderen Batch-Skripts/eines Unterprogramms in den Parametern jedes ^ durch ^^. Deshalb die Zeilen 6, 24 und 47. Beim Aufruf von der Kommandozeile findet diese Ersetzung jedoch nicht statt. Dadurch ergibt sich ein Unterschied beim Aufruf von der Eingabeaufforderung und beim Aufruf aus einem Batch-Skript. Wenn man z.B. ein Verzeichnis "E:\Test ^^ Test" hat, das man meinem Skript als Basisverzeichnis übergeben will, lautet der Aufruf von der Eingabeaufforderung (X=gewünschte Tiefe)
und aus einem Batch-Skript
Relative Pfade können durch %~f1 bei set "rootdir=%~f1" in Zeile 5 verarbeitet werden. Dadurch wird der Parameter %1 erstens von umgebenden Anführungszeichen befreit (die Tilde ~) und zweitens zu einem vollständigen Verzeichnispfad erweitert (das f). Aus . wird also das aktuell gesetzte Verzeichnis, aus .. das übergeordnete Verzeichnis des aktuellen Verzeichnisses, aus P: das aktuelle Verzeichnis von Laufwerk P:.

Die Funktion CountChr habe ich auch geändert, da sie noch einen Fehler enthielt, der zwar bei dem ursprünglichen Skript nie zum Tragen kam, aber jetzt ist sie allgemein verwendbar und gibt ihr Ergebnis in ERRORLEVEL zurück.

Hier das Skript:

Gruß
Friemler
Mitglied: Fahrrad
Fahrrad 18.08.2010 um 15:08:15 Uhr
Goto Top
Hallo Friemler,

die Einschränkungen sind mir soweit auch schon bei Pfaden mit den Sonderzeichen aufgefallen und jetzt läuft es fast so wie ich es möchte.....

Dein Skript möchte ich als Unterroutine in ein bestehendes Skript einbinden, d.h. Param1 und Param2 werden vorher schon als Directory (ohne abschließenden \) und Depth gesetzt.

Danke & Gruß
Fahrrad
Mitglied: Friemler
Friemler 18.08.2010 um 17:12:05 Uhr
Goto Top
Hallo Fahrrad,

Zitat von @Fahrrad:
... jetzt läuft es fast so wie ich es möchte.....

Wo hapert es denn noch?

Wenn Du ein Wurzelverzeichnis (z.B. P:\) als Basisverzeichnis angibst, ist der abschließende Backslash richtig und nötig. Also nicht pauschal einen abschließenden Backslash bei Parameter 1 entfernen.

Gruß
Friemler
Mitglied: Fahrrad
Fahrrad 19.08.2010 um 07:48:50 Uhr
Goto Top
Moin Friemler,

tja, da ist wohl wieder oder immer noch ein dickes brett....

ich möchte im Skript am Anfang (also sozusagen "hardcodiert")

set directory=P:\test und

Depth=4 setzen.

"Deine" Unterroutine soll dann bei Aufruf mit diesen beiden Variablen arbeiten.

Da hapert es noch ;o(

Danke & gruß
Fahrrad
Mitglied: Friemler
Friemler 19.08.2010 um 13:06:45 Uhr
Goto Top
Hallo Fahrrad,

ich weiß jetzt zwar immer noch nicht, wo das Problem liegt face-wink , aber mal ein paar Tipps ins Blaue.

  • Ersetze das @echo off in der ersten Zeile durch eine Sprungmarke, z.B. :DepthDir (Doppelpunkt nicht vergessen). Nimm auf jeden Fall einen Namen, der von den Sprungmarken, die im Skript schon verwendet werden, verschieden ist.

  • Die Sprungmarke muss am Zeilenanfang stehen, der Doppelpunkt ist also das erste Zeichen der Zeile.

  • Wenn Du am Anfang Deines Skripts
schreibst, lautet der Aufruf meines Unterprogramms
  • Wenn Du die Ausgabe meines Skripts weiterverarbeiten willst, um irgendwelche Operationen in den gelisteten Verzeichnissen auszuführen, musst Du das Skript z.B. als DepthDir.bat speichern (dann mit @echo off in der ersten Zeile, keine Sprungmarke). Der Aufruf wäre dann folgendermaßen, wenn DepthDir.bat im gleichen Verzeichnis wie Dein Hauptskript gespeichert ist:
Das würde die Namen der Dateien in den Verzeichnissen auflisten. Bei leeren Verzeichnissen oder Verzeichnissen, die nur Unterverzeichnisse enthalten, wird die Meldung "Das Verzeichnis ist leer oder enthält nur Unterverzeichnisse." ausgegeben.

Die Verwendung des Unterprogramms ListDir und darin der Variablen outdirname und dirname hat mit der Problematik der Zeichen ^&! zu tun. Da beim DIR-Befehl in Zeile 26 %dirname% in Anführungszeichen übergeben wird, kann es ruhig das Zeichen & enthalten. Für den ECHO-Befehl in Zeile 25 gilt das nicht, deshalb wird dort %outdirname% verwendet, in dem & escaped ist. Umgekehrt muss bei einem Pfad wie "P:\Test\Test ^ Test" (der an ListDir in der Form P:\Test\Test ^^ Test übergeben wird, siehe Erklärung bei geändertem neuem Skript) das doppelte ^ durch ein einfaches ^ ersetzt werden, damit DIR das Verzeichnis auch findet; ECHO benötigt aber das doppelte ^ im String (^ wird escaped), damit es in der Ausgabe nur noch einfach erscheint (wenn Du dich jetzt am Kopf kratzt, experimentiere mal ein wenig mit dem Code face-wink ).

Das obige Beispiel zeigt auch, was zu machen ist, wenn Du an den Verzeichnispfaden in der FOR-Schleife etwas manipulieren musst: Die Manipulation in ein Unterprogramm auslagern. Ansonsten musst Du mit SETLOCAL ENABLEDELAYEDEXPANSION arbeiten, in der FOR-Schleife die Laufvariable an eine Hilfsvariable zuweisen und die bei Stringmanipulationen mit ! einschließen. Und schon gibt es wieder Probleme mit Pfaden, die ein ! enthalten.


Vielleicht war ja ein Tipp dabei, der Dir hilft.



back-to-top[EDIT]
back-to-topIch habe mein neues Skript nochmal geändert, da es immer noch zu Fehlern mit dem Zeichen ^ in Pfadnamen kam.

Gruß
Friemler