For loop 仅在文件夹层次结构的子集中使用FOR/R进行递归搜索

For loop 仅在文件夹层次结构的子集中使用FOR/R进行递归搜索,for-loop,batch-file,cmd,For Loop,Batch File,Cmd,我想创建一个批处理文件,能够对文件夹层次结构中的每个JPG文件应用一些处理。下面的脚本文件非常适合这种情况(这里我只回显每个文件的名称,但在实际应用程序中应该用一些更复杂的语句来代替): 实际上,我不想浏览%basefolder%下的所有文件夹层次结构,只想浏览给定的子文件夹列表。此修改后的脚本能够处理这种情况: :VERSION 2 @echo off set "basefolder=C:\Base" set "subfolders=A B C" for

我想创建一个批处理文件,能够对文件夹层次结构中的每个JPG文件应用一些处理。下面的脚本文件非常适合这种情况(这里我只回显每个文件的名称,但在实际应用程序中应该用一些更复杂的语句来代替):

实际上,我不想浏览%basefolder%下的所有文件夹层次结构,只想浏览给定的子文件夹列表。此修改后的脚本能够处理这种情况:

:VERSION 2
@echo off
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
  pushd %basefolder%\%%~s"
  for /r %%f in (*.jpg) do echo %%f
  popd
)
是否有一种解决方案可以删除
pushd/popd
对语句,以使其更接近初始脚本。我认为以下脚本之一可以完成这项工作:

:VERSION 3
@echo off
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
  for /r %basefolder%\%%~s" %%f in (*.jpg) do echo %%f
)
或者,使用延迟扩展:

:VERSION 4
@echo off
setlocal enabledelayedexpansion
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
  set "folder=%basefolder%\%%~s"
  echo !folder!
  for /r !folder! %%f in (*.jpg) do echo %%f
)
但它们都不起作用。运行第二个时,
echo!文件夹命令按预期显示C:\Base\A、C:\Base\B和C:\Base\C,但内部循环不回显任何JPG文件,因此我猜/r
的递归
命令无法正确运行

我做错了什么


回答后的最终编辑:

感谢@aschipfl就另一个问题提供的信息,如下所述:

FOR
IF
REM
选项仅解析到特殊字符阶段。或者更好的方法是在特殊字符阶段检测命令,然后激活不同的解析器。因此,在这些选项中,既不能对
元变量使用延迟扩展,也不能使用


换句话说,我的版本3和4不起作用,因为在为/R
命令定义
根文件夹时,
%%~s
!文件夹。没有办法改变这一点,因为这是解析器的限制。正如我在下面的评论中所说的那样:
FOR/R
命令中的root folder选项基本上只是语法糖,以避免在命令前后使用
pushd/popd
。由于这种语法糖是不完整的,我们必须坚持一些特定用例的原始语法,如本文所示。@Gerhard(使用子例程
调用
)或@Mofi(解析
DIR
命令的结果)提出的备选方案正在发挥作用,但它们既不比我最初提出的简单的
pushd/popd
版本更具可读性,也没有更高的效率。

通常不建议将循环变量的值分配给环境变量,然后使用未经修改的环境变量,而不使用或与正在批量编码的其他字符串串联文件或已在FOR循环体中的FOR循环上方定义。这只会导致问题,因为它需要使用的结果是文件和文件夹中有一个或多个
FOR循环的内部不再正确处理代码>,或者在某些命令行上使用了命令
call
,或者使用调用
call
的子例程,这使得批处理文件的处理速度大大降低

我建议将此批处理文件用于此任务:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "basefolder=C:\Base"
set "subfolders=A B C "Subfolder D" SubfolderE"
for %%I in (%subfolders%) do for /F "delims=" %%J in ('dir "%basefolder%\%%~I\*.jpg" /A-D /B /S 2^>nul') do echo %%J
endlocal
子文件夹
中定义的每个子文件夹的内部FOR循环在后台启动另一个命令进程,并附加
%ComSpec%/c
DIR命令行作为附加参数。因此,在Windows安装到
C:\Windows
时执行,例如,对于第一个子文件夹:

C:\Windows\System32\cmd.exe /c dir "C:\Base\A\*.jpg" /A-D /B /S 2>nul
命令DIR搜索

  • 由于选项
    /S
  • 由于选项
    /A-D
    (属性不是目录)而导致的文件,包括设置了隐藏属性的文件
  • 在长或短文件名中匹配模式
    *.jpg
  • 由于选项
    /B
    (裸格式),输出处理后台命令的STDOUT,只处理匹配的文件名
  • 由于选项
    /S
    ,具有完整路径
DIR在未找到符合这些条件的内容时输出的错误消息正在从handleSTDERR重定向到deviceNUL以抑制它

阅读有关的Microsoft文档,以了解有关nul的说明。当Windows命令解释器在执行FOR的命令之前处理此命令行时,重定向操作符
必须在上用插入符号
^
转义,以便命令行被解释为文字字符,该命令行以单独的方式执行嵌入的dir命令行命令进程在后台启动

处理批处理文件的命令进程的输出分别由处理批处理文件的命令进程的捕获FOR在启动
cmd.exe
自身终止后逐行处理捕获的输出。这通常非常重要。在处理第一个文件名之前,要处理的文件列表已在command process的内存中。使用
for/R
的情况并非如此,因为这会导致访问文件系统,获取与通配符模式匹配的非隐藏文件的第一个文件名,运行for主体中的所有命令,并再次访问文件系统以获取下一个文件名。如果
for/R
在迭代这些条目时,由于文件系统中的条目发生了更改,而
for/R
的主体中的命令将文件更改为删除、移动、修改、复制到同一文件夹中,或重命名找到的文件,则
for/R
方法存在问题。这很容易导致
C:\Windows\System32\cmd.exe /c dir "C:\Base\A\*.jpg" /A-D /B /S 2>nul
@echo off
set "basedir=C:\Base"
set "subfolders="A","B","C""
for %%i in (%subfolders%) do for /R "%basedir%" %%a in ("%%~i\*.jpg") do echo %%~fa
set "subfolders="Folder A","Folder B","Folder C""
@echo off
set "basedir=C:\Base"
set "subfolders="A","B","C""
for %%i in (%subfolders%) do call :work "%%~i"
goto :eof
:work
for /R "%basedir%\%~1" %%a in (*.jpg) do echo %%~fa