【问题标题】:Batch create folder same filename批量创建文件夹相同的文件名
【发布时间】:2020-02-23 14:49:01
【问题描述】:

我正在使用此代码创建文件夹并统一具有相同名称的文件,结果发现某些文件可以正常工作,就像 3D 池的情况一样,但文件具有的其他情况(磁盘 1 of 2),(磁盘 2 of 2) 不起作用,可以帮忙吗?

@echo off
setlocal

set "basename=."
for /F "tokens=1* delims=." %%a in ('dir /B /A-D ^| sort /R') do (
   set "filename=%%a"
   setlocal EnableDelayedExpansion
   for /F "delims=" %%c in ("!basename!") do if "!filename:%%c=!" equ "!filename!" (
      set "basename=!filename!"
      md "!basename!"
   )
   move "!filename!.%%b" "!basename!"
   for /F "delims=" %%c in ("!basename!") do (
      endlocal
      set "basename=%%c
   )
)

我的结构文件夹与文件:

3D Pool (1989)(Firebird Software)(Disk 1 of 2).dsk
3D Pool (1989)(Firebird Software)(Disk 2 of 2).dsk
3D Pool (1989)(Firebird Software).dsk
3D Pool (1989)(Firebird Software)[cr Steel McKraken - Exocet].dsk
5th Axis, The (1985)(Loriciels)(fr).dsk
5th Axis, The (1985)(Loriciels)(fr)[a].dsk
5th Axis, The (1985)(Loriciels).dsk
Zolyx (1988)(Firebird Software).dsk
Zolyx (1988)(Firebird Software)[t].dsk
Zombi (1990)(Ubisoft)(fr)(Disk 1 of 2).dsk
Zombi (1990)(Ubisoft)(fr)(Disk 1 of 2)[6128 Version].dsk
Zombi (1990)(Ubisoft)(fr)(Disk 2 of 2).dsk
Zombi (1990)(Ubisoft)(fr)(Disk 2 of 2)[6128 Version].dsk
Zombi (1990)(Ubisoft)(fr)[464 Version].dsk

使用批处理时,它会创建文件夹并将文件放入其中:

3D Pool (1989)(Firebird Software)
3D Pool (1989)(Firebird Software)[cr Steel McKraken - Exocet]
5th Axis, The (1985)(Loriciels)
Zolyx (1988)(Firebird Software)
Zolyx (1988)(Firebird Software)[t]
Zombi (1990)(Ubisoft)(fr)(Disk 1 of 2)
Zombi (1990)(Ubisoft)(fr)(Disk 1 of 2)[6128 Version]
Zombi (1990)(Ubisoft)(fr)(Disk 2 of 2)
Zombi (1990)(Ubisoft)(fr)(Disk 2 of 2)[6128 Version]
Zombi (1990)(Ubisoft)(fr)[464 Version]

我希望批处理按名称将文件放在文件夹中,使其尽可能干净:

3D Pool (1989)(Firebird Software)
   3D Pool (1989)(Firebird Software)(Disk 1 of 2).dsk
   3D Pool (1989)(Firebird Software)(Disk 2 of 2).dsk
   3D Pool (1989)(Firebird Software).dsk
   3D Pool (1989)(Firebird Software)[cr Steel McKraken - Exocet].dsk
5th Axis, The (1985)(Loriciels)
   5th Axis, The (1985)(Loriciels)(fr).dsk
   5th Axis, The (1985)(Loriciels)(fr)[a].dsk
   5th Axis, The (1985)(Loriciels).dsk
Zolyx (1988)(Firebird Software)
   Zolyx (1988)(Firebird Software).dsk
   Zolyx (1988)(Firebird Software)[t].dsk
Zombi (1990)(Ubisoft)(fr)
   Zombi (1990)(Ubisoft)(fr)(Disk 1 of 2).dsk
   Zombi (1990)(Ubisoft)(fr)(Disk 1 of 2)[6128 Version].dsk
   Zombi (1990)(Ubisoft)(fr)(Disk 2 of 2).dsk
   Zombi (1990)(Ubisoft)(fr)(Disk 2 of 2)[6128 Version].dsk
   Zombi (1990)(Ubisoft)(fr)[464 Version].dsk

这样可以吗?

【问题讨论】:

    标签: file batch-file directory


    【解决方案1】:

    Mofi,非常感谢您对命令的帮助和解释,我遇到了一些问题

    发生的事情有些游戏不是这样的,游戏就是这样

    20000 Lieus sous les Mers (1988)(Coktel Vision)(fr)(Disk 1 of 2).dsk
    20000 Lieus sous les Mers (1988)(Coktel Vision)(fr)(Disk 2 of 2).dsk
    

    它们不在单个文件夹中,忽略 (Disk x of y) 文件夹列表:

    20000 Lieus sous les Mers (1988)(Coktel Vision)(fr)(Disk 1 of 2)
    20000 Lieus sous les Mers (1988)(Coktel Vision)(fr)(Disk 2 of 2)
    3 Guerra Mundial (1989)(Pactum)(es)(Disk 1 of 2)
    3 Guerra Mundial (1989)(Pactum)(es)(Disk 2 of 2)
    A la Conquete de l'Orthographe (1991)(Generation 5)(fr)(Disk 1 of 2)
    A la Conquete de l'Orthographe (1991)(Generation 5)(fr)(Disk 2 of 2)
    A la Pursuite de Carmen Sandiego dans le Monde (1990)(Broderbund Software)(fr)(Disk 1 of 2)
    A la Pursuite de Carmen Sandiego dans le Monde (1990)(Broderbund Software)(fr)(Disk 2 of 2)
    Zap 't' Balls - The Advanced Edition (1992)(Elmsoft Game-Service)(Disk 1 of 2)
    Zap 't' Balls - The Advanced Edition (1992)(Elmsoft Game-Service)(Disk 2 of 2)
    

    完整的文件夹列表:https://pastebin.com/UmZFR3mb

    我想了解为什么这些文件没有遵循 bat 文件模式

    【讨论】:

      【解决方案2】:
      @echo off
      
      for %%A in (*.dsk) do (
          for /f "tokens=1-3 delims=()" %%B in ("%%~A") do (
              if not exist "%%~B(%%~C)(%%~D)" md "%%~B(%%~C)(%%~D)"
              move /y "%%~A" "%%~B(%%~C)(%%~D)\" >nul
          )
      )
      

      对取自文件名的文件夹名称使用name (year)(publisher) 模式。

      【讨论】:

        【解决方案3】:

        此目录创建和文件移动任务可以使用以下批处理代码完成:

        @echo off
        if "%~1" == "" (pushd "%~dp0") else (
            pushd "%~1"
            if errorlevel 1 (
                echo ERROR: Directory "%~1" does not exist.
                echo/
                pause
                exit
            )
        )
        
        setlocal EnableExtensions DisableDelayedExpansion
        set "FolderName=\"
        
        for /F "eol=| delims=" %%I in ('dir /A-D /B /O-N 2^>nul') do if not "%%~fI" == "%~f0" (
            for /F "eol=| tokens=1 delims=[]" %%J in ("%%~nI") do (
                set "FileName=%%J"
                set "FullName=%%I"
                setlocal EnableDelayedExpansion
                set "DiskAddon=!FileName:*(Disk =(Disk !"
                if not "!DiskAddon!" == "!FileName!" for /F "delims=" %%V in ("!DiskAddon!") do set "FileName=!FileName:%%V=!"
                for /F "eol=| delims=" %%K in ("!FolderName!") do (
                    if "!FileName:%%K=!" == "!FileName!" (
                        md "!FileName!" 2>nul
                        move /Y "!FullName!" "!FileName!\"
                        for /F "eol=| delims=" %%V in ("!FileName!") do (
                            endlocal
                            set "FolderName=%%V"
                        )
                    ) else (
                        move /Y "!FullName!" "!FolderName!\"
                        endlocal
                    )
                )
            )
        )
        
        endlocal
        popd
        

        批处理文件可以存放在要处理的文件所在的目录中,也可以用要处理的目录的路径调用。将批处理文件目录或指定目录临时设为当前目录。

        环境变量FolderName 根据文件名存储最后创建的文件夹名称。使用无效名称 \ 定义环境变量 FolderName 以始终在第一个文件的最内部 IF 条件的 true 分支中运行。

        第一个 FOR 在后台运行另一个以%ComSpec% /c 开头的命令进程,' 之间的命令行作为附加参数附加。所以执行是在 Windows 安装到 C:\Windows:

        C:\Windows\System32\cmd.exe /c dir /A-D /B /O-N 2>nul
        

        启动的后台命令进程执行的命令DIR输出处理后台命令进程的STDOUT(标准输出)

        • 只是文件名,因为选项 /A-D(属性不是目录)
        • 裸格式,这意味着只是带有文件扩展名的文件名,但没有文件路径,因为选项/B
        • 由于/O-N而按名称反向排序
        • 当前目录中与默认通配符模式*匹配的所有文件。

        此处需要颠倒顺序,以便分别输出较短的文件名,分别输出具有[...] 的文件名,然后输出具有(Disk x of y) 的文件名。

        可能根本找不到文件。在这种情况下,DIR 会输出一条错误消息来处理 STDERR(标准错误)。通过将它重定向到设备 NUL 来抑制这个不重要的错误消息。 FOR 循环对在指定目录中找不到文件不执行任何操作。

        阅读有关Using command redirection operators 的Microsoft 文章,了解2>nul 的解释。重定向运算符 > 必须在 FOR 命令行上使用插入字符 ^ 转义,以便在 Windows 命令解释器在执行命令 FOR 之前处理此命令行时被解释为文字字符> 在后台启动的单独命令进程中执行嵌入的dir 命令行。

        FOR 使用选项 /F 和包含在 ' 中的命令行捕获所有输出以处理启动的后台命令进程的 STDOUT 并处理此输出启动后逐行cmd.exe在完成命令行执行后自行终止。

        FOR 默认忽略空行,此处不会出现。

        FOR 将默认使用普通空格和水平制表符作为字符串分隔符将每一行拆分为子字符串,并将仅将第一个空格/制表符分隔的子字符串分配给指定的循环变量I。此处不需要这种行拆分行为,因为文件名可以包含一个或多个空格。因此,使用选项 delims= 定义了一个空的分隔符列表,以完全禁用行拆分行为。

        FOR 也会忽略行拆分后的第一个子字符串是分号的行,分号是默认的行尾字符。文件名可以以分号开头。因此,eol=| 将行尾字符重新定义为竖线,文件名中不能包含该竖线。

        因此,每个带有文件扩展名的文件名都一个接一个地完全分配给循环变量I以供进一步处理。

        cmd.exe 当前处理的批处理文件在处理文件名时被忽略,因为第一个 FOR 命令行上的 IF 条件。

        下一个 FOR 只处理没有文件扩展名的文件名作为字符串。选项eol=| tokens=1 delims=[] 用于将文件名拆分为子字符串,使用方括号作为分隔符,仅将第一个子字符串分配给指定的循环变量J,并且不忽略以; 开头的文件名。这对于获取像3D Pool (1989)(Firebird Software)[cr Steel McKraken - Exocet].dsk 这样的文件名是必要的,只需将3D Pool (1989)(Firebird Software) 分配给循环变量J 以进行进一步处理。

        Variables are not behaving as expected 描述了 Windows 命令处理器在使用命令块执行命令之前,用环境变量的当前值替换命令块中以 ( 开头并以匹配 ) 结尾的所有 %variable% .这意味着 delayed expansion 是必需的,不能在 FOR 循环上方启用,否则文件名中的每个 ! 也将被解释为在执行时扩展的环境变量引用的开始/结束。出于这个原因,延迟扩展在循环内启用和禁用。
        阅读 this answer 了解有关命令 SETLOCALENDLOCAL 的详细信息。

        如果字符串(Disk  在文件名中的某个位置,则环境变量DiskAddon 使用文件名末尾的(Disk x of y) 部分定义,或者如果根本不包含(Disk ,则使用未修改的文件名。

        因此,如果分配给DiskAddon 的字符串与文件名字符串不同,则字符串(Disk x of y) 也会使用延迟扩展字符串替换从文件名中删除。分配给DiskAddon 的当前字符串必须临时分配给循环变量,以便能够在延迟的扩展字符串替换中使用此字符串。无法在延迟扩展环境变量替换中指定延迟扩展环境变量引用。

        当前文件夹名称也必须临时分配给循环变量,以便能够在下一个 IF 条件的延迟扩展字符串替换中使用此字符串。所以下一个 FOR 只是将当前文件夹名称分配给环境变量FolderName 分配给循环变量K

        IF 条件是当前(截断)文件名与所有出现的当前文件夹名称(不区分大小写)与当前(截断)文件名的比较,区分大小写。换句话说,IF 条件检查当前(截断的)文件名是否不包含当前文件夹名。在这种情况下,必须将当前文件移动到新文件夹中。

        因此,使用当前(截断)文件名创建文件夹,并通过重定向到设备 NUL 上的已存在文件夹并将当前文件移动到此文件夹中来抑制错误消息。接下来有必要禁用延迟扩展并恢复以前的环境。但是有必要将刚刚创建的文件夹的文件夹名称传递给以前的环境。因此,再次使用 FOR 循环,将当前文件夹名称分别截断的文件名分配给循环变量V,以获取分配给先前环境中环境变量FolderName 的文件夹名称。

        否则在禁用延迟扩展和恢复之前的环境之前,当前文件将移动到与之前文件相同的文件夹中。

        最终批处理文件恢复了初始环境和初始当前目录。


        这里还有一个使用子例程的替代解决方案,它使字符串替换更容易,因为命令块中没有命令行。

        @echo off
        if "%~1" == "" (pushd "%~dp0") else (
            pushd "%~1"
            if errorlevel 1 (
                echo ERROR: Directory "%~1" does not exist.
                echo/
                pause
                exit
            )
        )
        
        setlocal EnableExtensions DisableDelayedExpansion
        set "FolderName=\"
        for /F "eol=| delims=" %%I in ('dir /A-D /B /O-N 2^>nul') do if not "%%~fI" == "%~f0" call :ProcessFile "%%I"
        goto EndBatch
        
        :ProcessFile
        for /F "eol=| tokens=1 delims=[]" %%J in ("%~n1") do set "FileName=%%J"
        setlocal EnableDelayedExpansion
        set "DiskAddon=!FileName:*(Disk =(Disk !"
        if not "!DiskAddon!" == "!FileName!" set "FileName=!FileName:%DiskAddon%=!"
        if not "!FileName:%FolderName%=!" == "!FileName!" endlocal & goto MoveFile
        endlocal & set "FolderName=%FileName%"
        md "%FolderName%" 2>nul
        :MoveFile
        move /Y %1 "%FolderName%\"
        goto :EOF
        
        :EndBatch
        endlocal
        popd
        

        请参阅single line with multiple commands using Windows batch file,了解在此批处理文件中使用了两次运算符& 的说明,以避免使用命令块。


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

        • call /? ...解释%~dp0%~f0%1%~1%~n1
        • dir /?
        • echo /?
        • endlocal /?
        • exit /?
        • for /?
        • goto /?
        • if /?
        • md /?
        • move /?
        • pause /?
        • popd /?
        • pushd /?
        • set /?
        • setlocal /?

        PS:对整个任务使用 PowerShell 脚本会好得多。 PowerShell 支持内置字符串函数,例如在字符串中搜索字符串或使用正则表达式修改字符串以获取基本文件名。执行批处理文件的 Windows 命令处理器旨在执行命令和可执行文件,而不是用于此任务所需的字符串操作。

        【讨论】:

        • 莫菲,感谢文件夹生成过程中发生的一些事情,我作为另一个答案报告了它
        • @Alvarez 您还没有写任何关于将哪些规则应用于文件名以将它们分组并将它们移动到文件夹中的任何内容。出于这个原因,我根据与初始代码相同的规则编写批处理文件,并在第一次出现方括号时截断文件名。如果分组规则如下所示,我会以不同的方式编写批处理文件: 1. 从所有文件名中删除 (Disk x of y)xy 可以是任何正数。 2.从所有文件名中删除[z Version]z可以是任何正数。
        • 我找到了一个解决方案,可以从每个文件名的末尾删除 (Disk x and y) 以支持两条规则中的第一条规则。所以批处理文件现在应该可以工作了。请不要要求删除(fr),因为在您的示例中,有一次要保留(fr),要删除一次(fr)。只有使用人工智能才能找到透视文件夹名称,但结果可能与您的预期不同(因为您的大脑正在使用不同的人工算法)。
        猜你喜欢
        • 2016-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多