首页 > 解决方案 > 批量从数组中获取目录名称

问题描述

我有一个要从中提取文件夹名称的路径列表

我写:

@echo off

set paths[0]="C:\p\test1" 
set paths[1]="C:\p\test2" 
set paths[2]="C:\p\test3"

(for %%p in (%paths%) do (
   for %%F in (%%p) do echo Processing %%~nxF
))

但似乎什么也没显示。

我希望看到:

处理测试1

处理测试2

加工测试3

标签: batch-file

解决方案


如果 first"在命令行上指定set给变量名或留给变量值,则会产生很大的不同。在大多数情况下,最好将其指定给变量名,特别是如果稍后应该将保存路径的变量值与文件名连接到完整的限定文件名。

另请参阅:在命令行上使用 'set var = text' 后,为什么没有带有 'echo %var%' 的字符串输出?

此任务的解决方案是:

@echo off

set "paths[0]=C:\p\test1"
set "paths[1]=C:\p\test2"
set "paths[2]=C:\p\test3"

for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ

带有选项的命令FOR/F和包含在中的集合'导致启动另一个在后台运行的命令进程,%ComSpec% /c并在两个'附加的参数之间指定命令行。因此,在这种情况下,Windows 安装到C:\Windows

C:\Windows\System32\cmd.exe /c set paths[ 2>nul

命令SET以处理启动后台命令进程的STDOUTpaths[格式逐行输出所有名称以开头的环境变量。VariableName=VariableValue

可能是没有名称开头的环境变量paths[会导致错误消息输出以通过命令SET处理STDERR ,该命令将从后台命令进程重定向到处理正在处理批处理文件的命令进程的STDERR和因此将显示在控制台窗口中。由于这个原因,后台命令进程将可能的错误消息重定向到设备NUL以使用.2>nul

阅读有关使用命令重定向运算符的Microsoft 文章,了解2>nul. 当 Windows 命令解释器在执行命令FOR之前在后台启动的单独命令进程中执行嵌入式命令行时,重定向运算符>必须^FOR命令行上使用脱字符进行转义,以将其解释为文字字符。set

在这种情况下, FOR捕获为处理已启动后台命令进程的STDOUT而编写的所有内容,并在启动后逐行处理此输出cmd.exe

FOR忽略空行,这在此处无关紧要,因为没有要处理的空行。

FOR将使用普通空格和水平制表符作为字符串分隔符将非空行拆分为子字符串,并且如果它不以默认的行尾字符开头,则只会将第一个空格/制表符分隔的字符串分配给指定的循环变量;。此处不需要此默认行拆分行为。因此,该选项delims==将等号定义为字符串分隔符。

在这种情况下,该选项tokens=1*指示FOR将变量名称分配给指定的循环变量I,并在变量名称之后的等号之后分配所有内容,而不根据ASCII 表在等号上进一步将字符串拆分到下一个循环变量,在这种情况下J. 这就是为什么循环变量被解释为区分大小写而环境变量由 Windows 命令处理器处理而不区分大小写的原因。

在这种情况下,只有变量值在FOR循环的主体中是有意义的。因此,JECHO命令行上只使用循环变量,而I根本不使用。

修饰符%~nxJ导致从分配给循环变量的字符串值中删除周围的双引号,J然后在最后一个反斜杠或字符串开头之后获取字符串,以防字符串值根本不包含反斜杠。这是文件夹路径字符串中最后一个文件夹的名称。

要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

  • echo /?
  • for /?
  • set /?

更新:

与迄今为止发布的其他两个解决方案相比,此解决方案有一个很大的优势:

没有使用延迟的环境变量扩展,这在处理文件或文件夹名称时总是有问题,因为不能 100% 确定没有文件夹和文件的名称中包含感叹号。

让我们比较三个解决方案,其中包含不寻常的文件夹名称!

@echo off
rem Make sure there is no environment variable defined of which name starts with
rem paths[ as suggested by Compo which is a very valuable addition on my code.
for /F "delims==" %%I in ('set paths[ 2^>nul') do set "%%I="

set "paths[0]=C:\p\test1!"
set "paths[1]=C:\p\!test2"
set "paths[2]=C:\p\!test!3"

echo/
echo Results of solution 1:
echo/
for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ

echo/
echo Results of solution 2:
echo/
SetLocal EnableDelayedExpansion
for /L %%i in (0,1,2) do (
    for %%j in (!paths[%%i]!) do echo Processing %%~nxj
)
endLocal

echo/
echo Results of solution 3:
echo/
Setlocal EnableDelayedExpansion
Call :process paths "!paths[0]!" "!paths[1]!" "!paths[2]!"
Endlocal
echo/
pause
goto :EOF

:process
Set P_C=0
Set /a P_C-=1

For %%a in (%*) DO (
    CALL :populate %1 "%%~a"
)

Set /a P_C-=1

For /L %%b in (0,1,!P_C!) DO (
    ECHO Processing %1[%%b] = "!%1[%%b]!"
)
GOTO :EOF

:populate
Set "%1[!P_C!]=%~2"
Set /a P_C+=1
GOTO :EOF

运行此批处理文件的输出是:

Results of solution 1:

Processing test1!
Processing !test2
Processing !test!3

Results of solution 2:

Processing test1
Processing test2
Processing 3

Results of solution 3:

Processing paths[0] = "C:\p\test1\p\\p\3"

此处发布的解决方案 1 适用于所有三个文件夹名称正确。

解决方案 2 省略了第一个和第二个文件夹名称的感叹号,这很可能导致进一步处理时出错。第三个文件夹名称被修改为完全不同的名称。启用延迟扩展会导致在被替换echo Processing %%~nxj后第二次解析,现在将文件夹名称解释为环境变量名称,其值被延迟引用。运行此批处理文件时没有定义环境变量,因此在Windows 命令处理器执行之前就变成了。%~nxj!test!3testtest!test!33echo

解决方案 3 会在任何包含感叹号的文件夹名称上产生垃圾,即使是在启用延迟扩展之前定义的完整限定文件夹名称上,并在调用子例程时使用延迟扩展引用process

幸运的是,名称中带有感叹号的文件夹和文件名很少见,这使得延迟扩展的使用通常没有问题。但我想在这里提一下,任何包含一个或多个!.


推荐阅读