批量从数组中获取目录名称

Sna*_*yes 5 batch-file

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

我写:

@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
))
Run Code Online (Sandbox Code Playgroud)

但似乎什么也没有显示。

我期望看到:

加工测试1

加工测试2

加工测试3

Mof*_*ofi 2

"如果在命令行上指定set变量名称左侧或变量值左侧,则会产生很大的差异。在大多数情况下,最好将其指定为变量名,特别是当保存路径的变量值稍后应与文件名连接到完整限定文件名时。

\n

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

\n

该任务的解决方案是:

\n
@echo off\n\nset "paths[0]=C:\\p\\test1"\nset "paths[1]=C:\\p\\test2"\nset "paths[2]=C:\\p\\test3"\n\nfor /F "tokens=1* delims==" %%I in (\'set paths[ 2^>nul\') do echo Processing %%~nxJ\n
Run Code Online (Sandbox Code Playgroud)\n

带有选项和一组括起来的命令FOR会导致启动另一个在后台运行的命令进程,并在两个附加参数之间指定命令行。因此在本例中执行的是 Windows 安装到:/F\'%ComSpec% /c\'C:\\Windows

\n
C:\\Windows\\System32\\cmd.exe /c set paths[ 2>nul\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

请阅读有关使用命令重定向运算符的Microsoft 文章,了解 的说明2>nul当 Windows 命令解释器在执行命令FOR之前处理此命令行时,\xc2\xa0>重定向运算符必须^FOR命令行上使用脱字符号进行转义,以便将其解释为文字字符,该命令使用在后台启动的单独命令进程来执行嵌入的命令行。set

\n

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

\n

FOR会忽略空行,这在这里并不重要,因为没有要处理的空行。

\n

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

\n

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

\n

在这种情况下, FOR循环体中仅对变量值感兴趣。因此,ECHO命令行J上仅使用循环变量,而根本不使用循环变量。I

\n

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

\n

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

\n
    \n
  • echo /?
  • \n
  • for /?
  • \n
  • set /?
  • \n
\n
\n

更新:

\n

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

\n

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

\n

让我们比较这三个解决方案与不寻常的文件夹名称包含!.

\n
@echo off\nrem Make sure there is no environment variable defined of which name starts with\nrem paths[ as suggested by Compo which is a very valuable addition on my code.\nfor /F "delims==" %%I in (\'set paths[ 2^>nul\') do set "%%I="\n\nset "paths[0]=C:\\p\\test1!"\nset "paths[1]=C:\\p\\!test2"\nset "paths[2]=C:\\p\\!test!3"\n\necho(\necho Results of solution 1:\necho(\nfor /F "tokens=1* delims==" %%I in (\'set paths[ 2^>nul\') do echo Processing %%~nxJ\n\necho(\necho Results of solution 2:\necho(\nSetLocal EnableDelayedExpansion\nfor /L %%i in (0,1,2) do (\n    for %%j in (!paths[%%i]!) do echo Processing %%~nxj\n)\nendLocal\n\necho(\necho Results of solution 3:\necho(\nSetlocal EnableDelayedExpansion\nCall :process paths "!paths[0]!" "!paths[1]!" "!paths[2]!"\nEndlocal\necho(\npause\nexit /B\n\n:process\nSet P_C=0\nSet /a P_C-=1\n\nFor %%a in (%*) DO (\n    CALL :populate %1 "%%~a"\n)\n\nSet /a P_C-=1\n\nFor /L %%b in (0,1,!P_C!) DO (\n    ECHO Processing %1[%%b] = "!%1[%%b]!"\n)\nGOTO :EOF\n\n:populate\nSet "%1[!P_C!]=%~2"\nSet /a P_C+=1\nGOTO :EOF\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
Results of solution 1:\n\nProcessing test1!\nProcessing !test2\nProcessing !test!3\n\nResults of solution 2:\n\nProcessing test1\nProcessing test2\nProcessing 3\n\nResults of solution 3:\n\nProcessing paths[0] = "C:\\p\\test1\\p\\\\p\\3"\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

解决方案 3 在任何包含 \xc2\xa0 感叹号的文件夹名称上都会产生垃圾,即使在启用延迟扩展之前定义的完全限定文件夹名称上以及在调用子例程时通过延迟扩展引用也是如此process

\n

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

\n