подсчитать количество подкаталогов в подкаталогах - PullRequest
0 голосов
/ 30 апреля 2018

Я смотрю, чтобы увидеть максимальное количество подкаталогов в любом подкаталоге в папке. Т.е.

Folder==>
    SubA==>
        b.xlsx
    SubB==>
        SubB.C==>
           b.c.xlsx

Так что это вернет (2), поскольку SubB.C имеет глубину в две папки.

Я пробовал

set count=
for /d %%a in (U:\*) do set /a count+=1
echo %count%

Но рекурсия ставит меня в тупик Я не могу попасть в подкаталоги второго / третьего / четвертого уровня.

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

Я не имею представления о производительности, (только проблема с большими деревьями каталогов) , но вы можете попробовать метод, основанный на PowerShell, также из вашего пакетного файла:

@Echo Off
CD /D "U:\" 2>Nul || Exit /B
PowerShell -NoL -NoP "GCI .\ -R|?{$_.PSIsContainer}|Select @{Name='FullName';Expression={$_.FullName.Replace($PWD,'')}},@{Name='FolderDepth';Expression={($_.FullName.Split('\').Count)-($PWD.Path.Split('\').Count)}}|Sort -Des FolderDepth|Select -F 1 -Exp FolderDepth"
Pause

Редактировать (для удовлетворения требований в комментарии к литу) :

@Echo Off
CD /D "U:\" 2>Nul || Exit /B
Set "MaxLevels="
For /F %%A In ('
    PowerShell -NoL -NoP "GCI .\ -R|?{$_.PSIsContainer}|Select @{Name='FullName';Expression={$_.FullName.Replace($PWD,'')}},@{Name='FolderDepth';Expression={($_.FullName.Split('\').Count)-($PWD.Path.Split('\').Count)}}|Sort -Des FolderDepth|Select -F 1 -Exp FolderDepth"
') Do Set "MaxLevels=%%A"
Set MaxLevels 2>Nul
Pause
0 голосов
/ 30 апреля 2018

Вы можете взломать JREPL.BAT (текстовый процессор регулярных выражений), чтобы подсчитать количество обратных слешей в каждом пути. Сначала я перечисляю корневую папку, чтобы можно было вычесть это значение из максимума, чтобы получить максимальную глубину вложенной папки в корневой папке.

@echo off
pushd %1
(cd & dir /b /s /ad) | jrepl "\\" "$txt=false;n++" ^
  /JMATCHQ ^
  /JBEG "var n, root, max=0" ^
  /JBEGLN "n=0" ^
  /JENDLN "if (ln==1) root=n; if (n>max) max=n" ^
  /JEND "output.writeLine(max-root)"
popd

Я использовал множество ^ продолжения строки, чтобы код легко читался без горизонтальной прокрутки.

Если вы хотите записать результат в переменную, то самое простое решение - записать выходные данные CD и DIR во временный файл и использовать опцию JREPL / RTN для сохранения результата.

@echo off
setlocal
set "file=%temp%\folders.txt"
pushd %1
(cd & dir /b /s /ad) >"%file%"
popd
call jrepl "\\" "$txt=false;n++" /F "%file%" ^
  /JMATCHQ ^
  /JBEG "var n, root, max=0" ^
  /JBEGLN "n=0" ^
  /JENDLN "if (ln==1) root=n; if (n>max) max=n" ^
  /JEND "output.writeLine(max-root)" ^
  /RTN count
del "%file%"
echo %count%

На моей машине мой код работает примерно в два раза быстрее, чем решение Powerpo от Compo, и на намного, намного * на 1013 * быстрее, чем рекурсивное чистое пакетное решение aschipfl.


Вот гибридное JScript / пакетное решение, для которого не требуется JREPL.BAT

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

::--- Batch section within JScript comment that calls the internal JScript ----
@echo off
pushd %1 || exit /b
(cd & dir /b /s /ad) | cscript //E:JScript //nologo "%~f0"
popd
exit /b

----- End of JScript comment, beginning of normal JScript  ------------------*/
var root=0, max=0, level;
while( !WScript.StdIn.AtEndOfStream ) {
  level=WScript.StdIn.ReadLine().split('\\').length-root;
  if (!root) root=level;
  if (level>max) max=level;
}
WScript.StdOut.WriteLine(max);

Если вы хотите записать результат в переменную, вы можете использовать цикл FOR / F:

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

::--- Batch section within JScript comment that calls the internal JScript ----
@echo off
pushd %1 || exit /b
for /f %%A in ('(cd ^& dir /b /s /ad^) ^| cscript //E:JScript //nologo "%~f0"') do set depth=%%A
popd
set depth
exit /b

----- End of JScript comment, beginning of normal JScript  ------------------*/
var root=0, max=0, level;
while( !WScript.StdIn.AtEndOfStream ) {
  level=WScript.StdIn.ReadLine().split('\\').length-root;
  if (!root) root=level;
  if (level>max) max=level;
}
WScript.StdOut.WriteLine(max);
0 голосов
/ 30 апреля 2018

Вам нужно использовать какую-то рекурсию для этой задачи. Как насчет подпрограммы, которая перебирает подкаталоги и вызывает себя для каждого? Я имею в виду следующее:

@echo off
rem // Define constants here:
set "_PATH=%~1"   & rem // (path of the root directory to process)
rem // Define global variables here:
set /A "$DEPTH=0" & rem // (variable to determine the greatest depth)

rem // Initialise variables:
set /A "DEEP=0" & rem // (depth of the current directory branch)
rem // Call recursive sub-routine, avoid empty argument:
if defined _PATH (call :SUB "%_PATH%") else (call :SUB ".")
rem // Return found depth:
echo %$DEPTH%
exit /B

:SUB  <root_path>
rem // Loop through all sub-directories of the given one:
for /D %%D in ("%~1\*") do (
    rem // For each sub-directory increment depth counter:
    set /A "DEEP+=1"
    rem // For each sub-directory recursively call the sub-routine:
    call :SUB "%%~fD"
)
rem // Check whether current branch has the deepest directory hierarchy:
if %$DEPTH% lss %DEEP% set /A "$DEPTH=DEEP"
rem // Decrement depth counter before returning from sub-routine:
set /A "DEEP-=1"
exit /B

Точно так же, как альтернативная идея, но с немного худшей производительностью, вы также можете определить количество обратных слешей (\) в разрешенных путях всех подкаталогов, получить наибольшее число и вычесть это число корня. каталог от самого большого, как это:

@echo off
rem // Define constants here:
set "_PATH=%~1"   & rem // (path of the root directory to process)
rem // Define global variables here:
set /A "$DEPTH=0" & rem // (variable to determine the greatest depth)

rem // Change to root directory:
pushd "%_PATH%" || exit /B 1
rem // Resolve root directory:
call :SUB "."
rem // Store total depth of root directory:
set /A "CROOT=$DEPTH, $DEPTH=0"
rem // Process all sub-directories recursicely:
for /D /R %%D in ("*") do (
    rem // Determine greatest depth relative to root:
    call :SUB "%%~fD" -%CROOT%
)
rem // Change back to original directory:
popd
rem // Return found depth:
echo %$DEPTH%
exit /B

:SUB  <val_path>  [<val_offset>]
rem // Resolve provided sub-directory:
set "ITEM=%~f1" & if not defined ITEM set "ITEM=."
rem // Initialise variables, apply count offset:
set "COUNT=%~2" & set /A "COUNT+=0"
rem // Count number of backslashes in provided path:
for %%C in ("%ITEM:\=" "%") do (
    set /A "COUNT+=1"
)
rem // Check whether current branch has the deepest directory hierarchy:
if %$DEPTH% lss %COUNT% set /A "$DEPTH=COUNT"
exit /B
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...