将文件夹移至它们对应首字母的目录下

4

我使用这个脚本将文件夹移动到以它们首字母命名的目录下。

@echo off
setlocal enabledelayedexpansion

tree
for /d %%i in (*) do (
  set first=%%i
  set first=!first:~0,1!
  md !first! 2>nul
  if not "!first!" == "%%i" move "%%i" "!first!\%%i" 
)
tree

这个脚本会生成首字母目录。例如,如果我的文件夹名字是这样的

Wachenfeldt - The
Пламень - Чужие
Von Stroheim - Lov

这个脚本生成的首字母目录可以是:

W
П
V

如果首字母目录已经存在,我没有测试此脚本是否能正常运行

问题在哪里?脚本只起到了部分作用,因为许多文件夹没有移动到它们自己的首字母目录中。在CMD中,我读到了这个错误信息:

文件名或扩展名过长

我的结果

被移动的文件夹名称示例

Wachenfeldt - The Interpreter (2019) Progressive Death Metal
Whitby Bay - Gothic Attack Vehicle (2018) Black Metal
Пламень - Чужие (2017) Black Metal (D5)

W
|
|----  Wachenfeldt - The Interpreter....
|----- Whitby Bay - Gothic Attack....

П
|
|----- Пламень - Чужие (20...

以下是不会被移动(脚本无法处理)的文件夹名称示例:

Withering Night - Within The Shadows We Lurk (2013) Black Metal (D5)
Woltemade - Siele Vir Die See (2019) Melodic Death Metal
Von Stroheim - Love_ Who Gets Love_ (2019) Doom_Post-Metal
1个回答

2
我建议为这个文件夹移动任务使用以下批处理代码:
@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 /d %%i in (*) do”命令,那么“FOR”命令将直接从文件系统逐个获取文件夹名称,并且由于在循环内部创建了一个额外的文件夹并将文件夹移动到子文件夹,这个列表会随着每次执行循环内的命令而发生变化。这可能导致一个文件夹被多次处理(这里没有问题),或者因为当前文件夹中的文件夹更改而被跳过,而“FOR”正在遍历列表。特别是在FAT32和exFAT驱动器上,目录条目不按字母顺序进行本地排序,因此非常重要的是首先将完整的文件夹列表加载到内存中,然后开始遍历内存中的文件夹列表,而不受在执行循环中的命令期间对文件系统的修改的影响。
使用选项/FFOR在此处的结果是在后台启动一个额外的命令进程,使用%ComSpec% /c和附加的命令行'作为额外的参数。因此,在安装了Windows的C:\Windows中执行。
C:\Windows\System32\cmd.exe /c dir /AD /B 2>nul

命令DIR由后台命令进程执行,在当前目录中搜索:
- 由于选项/AD(目录属性),仅搜索文件夹,包括具有隐藏属性的文件夹,这些文件夹被for /D忽略; - 由于选项/B,仅以裸格式输出文件夹名称,不包含路径。
请阅读Microsoft文档使用命令重定向运算符,了解2>nul的解释。在FOR命令行中,重定向运算符>必须用插入字符^进行转义,以便在Windows命令解释器处理此命令行之前,将其解释为字面字符。然后FOR命令执行嵌入的dir命令行,并使用单独启动的后台命令进程来执行该命令。
处理启动命令进程的STDOUT所写入的输出被批处理文件处理的命令进程捕获,并在命令进程终止后,由FOR逐行解释。命令进程在执行完命令DIR后自行终止。
使用选项/FFOR默认忽略空行,这对本例没有问题。一行被默认的普通空格和水平制表符分隔成子字符串。通过设置delims=为空的分隔符列表来禁用此行分割行为,因为文件夹名称可能包含一个或多个空格。如果第一个子字符串(在本例中即整行)以默认的行尾字符;开头,则该行也会被忽略,而这里不希望文件夹名称以分号开头。eol=|将行尾字符重新定义为竖线,而文件夹名称永远不会包含竖线,例如?*也可以使用。

延迟扩展在将当前文件夹名称分配给环境变量时未启用,否则包含一个或多个!的文件夹名称将无法正确处理批处理文件。Windows命令处理器会将文件夹名称中的感叹号解释为命令行上延迟扩展环境变量引用的开始/结束set "FolderName=%%I",如果在处理此命令行时已经启用了延迟环境变量扩展。

另请参阅:Windows命令解释器(CMD.EXE)如何解析脚本?

但是,为了进一步的命令,需要启用延迟扩展,如变量的行为不符合预期所述,因此使用命令setlocal EnableDelayedExpansion来启用延迟扩展。

重要的是,在以下命令行中始终使用带有延迟扩展的环境变量FolderName进行引用,并且不要使用%%I来处理包含一个或多个感叹号的正确文件夹名称。

当前处理的文件夹将被移动到一个子文件夹中,该子文件夹的名称是当前文件夹名称的第一个字符,前提是当前文件夹名称不是一个单字符文件夹名称,并且目标文件夹的创建成功,当前文件夹确实可以被移动,因为没有正在运行的进程使用此文件夹或其子文件夹作为当前文件夹,并且没有正在运行的进程打开了此文件夹或其子文件夹中的任何文件,阻止了文件的删除或移动,只要该文件被进程打开,而且目标文件夹中没有已经存在同名的文件夹。
然后,在处理内存中的下一个文件夹名称之前,必须恢复先前的执行环境。有关命令"SETLOCAL"和"ENDLOCAL"的详细信息,请阅读this answer。因为在列表中的每个文件夹名称上启用和禁用延迟扩展所做的工作不仅仅是这些。
为了理解所使用的命令及其工作原理,请打开一个命令提示符窗口,在其中执行以下命令,并完整、仔细地阅读每个命令显示的帮助页面。
  • dir /?
  • echo /?
  • endlocal /?
  • for /?
  • if /?
  • md /?
  • move /?
  • set /?
  • setlocal /?
  • tree /?

注意:此批处理文件解决方案不适用于包含Unicode字符的文件夹。如果文件夹包含非ASCII字符,则应使用类似this onemklement0编写的PowerShell脚本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接