我建议为这个文件夹移动任务使用以下批处理代码:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
%SystemRoot%\System32\tree.com
for /F "eol=| delims=" %%I in ('dir /AD /B 2^>nul') do (
set "FolderName=%%I"
setlocal EnableDelayedExpansion
set "TargetFolder=!FolderName:~0,1!"
if not "!TargetFolder!" == "!FolderName!" (
md "!TargetFolder!" 2>nul
move /-Y "!FolderName!" "!TargetFolder!\"
)
endlocal
)
%SystemRoot%\System32\tree.com
endlocal
对于将文件夹移动到子文件夹的任务,最重要的是首先将整个文件夹名称列表加载到内存中在使用 FOR 循环处理每个文件夹名称之前。在仅使用for /d %%i in (*) do 时,命令 FOR 会遍历文件夹名称列表,直接从文件系统中按名称获取名称,并且该列表会随着循环内命令的每次执行而更改,因为可能会创建一个附加文件夹并将一个文件夹移动到一个子文件夹中。这很容易导致一个文件夹被多次处理(这里没问题)或者由于当前文件夹中的文件夹更改而被跳过,而 FOR 遍历列表。特别是在 FAT32 和 exFAT 驱动器上,目录条目未按字母顺序本地特定排序,首先获取加载到内存中的完整文件夹列表,然后开始遍历内存中不受影响的列表中的文件夹,这是非常重要的通过在循环中执行命令期间对文件系统的修改。
FOR 带有选项/F 会在后台启动另一个命令进程,其中%ComSpec% /c 和' 中的命令行作为附加参数附加。因此在后台执行,Windows 安装到 C:\Windows:
C:\Windows\System32\cmd.exe /c dir /AD /B 2>nul
后台命令进程执行的命令DIR在当前目录中搜索
- 只是文件夹,因为选项
/AD(属性目录)包括隐藏属性集被 for /D 忽略的文件夹和
- 由于选项
/B,仅以裸格式输出没有路径的文件夹名称。
阅读有关Using command redirection operators 的Microsoft 文档以了解2>nul 的解释。重定向运算符 > 必须在 FOR 命令行上使用插入字符 ^ 转义,以便在 Windows 命令解释器在执行命令 FOR 之前处理此命令行时被解释为文字字符> 在后台启动的单独命令进程中执行嵌入的dir 命令行。
为处理已启动命令进程的STDOUT而写入的输出由处理批处理文件的命令进程捕获,并由FOR在完成命令 DIR 的执行后,started 命令进程自行终止。
FOR 带有选项 /F 默认忽略空行,这在这里没有问题。默认情况下,使用普通空格和水平制表符作为字符串分隔符将一行拆分为子字符串。使用delims= 禁用该行拆分行为,它定义了一个空的分隔符列表,因为文件夹名称可以包含一个或多个空格。如果第一个子字符串(在这种情况下 = 整行)以默认的行尾字符 ; 开头,则该行也会被忽略,因为文件夹名称可以以分号开头,因此此处不需要。 eol=| 将行尾字符重新定义为竖线,任何文件夹名称都不能包含像 ? 或 * 这样也可以使用的竖线。
Delayed expansion 在将当前文件夹名称分配给环境变量时未启用,否则批处理文件将无法正确处理包含一个或多个 ! 的文件夹名称。如果在处理此命令行时已启用延迟环境变量扩展,Windows 命令处理器会将文件夹名称中的感叹号解释为命令行 set "FolderName=%%I" 上延迟扩展环境变量引用的开始/结束。
另见:How does the Windows Command Interpreter (CMD.EXE) parse scripts?
但是有必要为进一步的命令启用延迟扩展,例如Variables are not behaving as expected,因此命令setlocal EnableDelayedExpansion现在用于启用延迟扩展。
重要的是在以下命令行中始终引用具有延迟扩展的环境变量FolderName,并且不要使用%%I 来处理带有一个或多个感叹号的正确文件夹名称。
如果当前文件夹名称还不是单字符文件夹名称,则将当前处理的文件夹移动到名称为当前文件夹名称第一个字符的子文件夹中,当然目标文件夹的创建完全成功,并且当前文件夹可以真正移动,因为没有正在运行的进程使用此文件夹或其子文件夹之一作为当前文件夹,并且没有正在运行的进程在此文件夹或其子文件夹中打开任何文件,只要是由进程打开,并且目标文件夹不包含同名文件夹。
那么在处理内存列表中的下一个文件夹名称之前,必须恢复之前的执行环境。阅读 this answer 了解有关命令 SETLOCAL 和 ENDLOCAL 的详细信息,因为除了为列表中的每个文件夹名称启用和禁用延迟扩展之外,还有更多工作要做。
要了解所使用的命令及其工作原理,请打开command prompt 窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。
dir /?
echo /?
endlocal /?
for /?
if /?
md /?
move /?
set /?
setlocal /?
tree /?
注意:此批处理文件解决方案不适用于包含 Unicode 字符的文件夹。如果文件夹包含非ASCII 字符,则应使用mklement0 编写的类似this one 的PowerShell 脚本。