count子目录中的子目录数

时间:2018-04-30 09:25:35

标签: batch-file cmd

我希望查看文件夹中任何子目录中的最大子目录数。 即,

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 :(得分:3)

我不知道性能,(只有大目录树的问题),但您也可以尝试从批处理文件中使用基于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

答案 1 :(得分:3)

您可以破解JREPL.BAT(正则表达式文本处理器)来计算每个路径中的反斜杠数。我首先列出根文件夹,以便我可以从max中减去该值,以获得根文件夹中的最大子文件夹深度。

@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%

在我的机器上,我的代码运行速度是Compo的PowerShell解决方案的两倍,并且比aschipfl递归纯批处理解决方案快得多

这是一个不需要JREPL.BAT的混合JScript /批处理解决方案

@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);

答案 2 :(得分:2)

您需要为该任务使用某种递归。如何循环子目录并为每个子目录调用自身的子例程呢?我的意思是:

@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