批量重命名文件,包括完整的父目录结构

Batch rename of files including complete parent directory structure

我有一个相当广泛的文件夹结构,deepest/last 文件夹中只有一个文件。文件名:model.cgf

我想要的是有一个命令行批处理文件来重命名所有文件,重命名它们以便它们列出它们的完整父文件夹结构。然后将所有文件复制到一个指定的文件夹中。 复制的文件将充当目录。所以我知道,在查看文件时,它是文件夹结构中的确切位置。 如果脚本在再次 运行 时可以仅添加较新的文件,那就太棒了。

    Here is an example of the current file/folder structure:

    _All_Models
    |  |__Script.bat
    |
    |__Catalog
    |
    |__Models
       |
       |__SubFolder
       |  |__model.cfg 
       |  |
       |  |__SubFolder
       |  |  |__model.cfg
       |  |
       |  |__SubFolder
       |     |__SubFolder
       |     |  |__model.cfg
       |     |
       |     |__SubFolder
       |        |__SubFolder
       |           |__SubFolder
       |              |__model.cfg
       |
       |__etc.
       |  |__etc.
       |
       |__etc.

这是我想要的示例:

    _All_Models
    |  |__Script.bat
    |
    |__Catalog
    |  |__SubFolder.cfg 
    |  |__SubFolder_SubFolder.cfg
    |  |__SubFolder_SubFolder_SubFolder.cfg
    |  |__SubFolder_SubFolder.cfg
    |  |__SubFolder_SubFolder_SubFolder_SubFolder.cfg
    |  |__etc.
    |  |__etc.
    |  |__etc.
    |
    |__Models
       |__etc.
       |__etc.
       |__etc.

我目前能做的就是将文件"model.cfg"批量重命名为"parent-folder.cfg",然后复制到指定的文件夹中。这将重命名和合并所有文件,但是我现在丢失了所有以前的父文件夹并且不知道文件在文件夹结构中的位置。

我对脚本知之甚少,所以我做了很多搜索来找到有用的东西,但也许我想要的东西是不可能的。 这就是我想出的:

    CD Models

    REM Rename
    for /r %%D in (.) do (for /f "delims=" %%F in ('dir /b /A:-D "%%D"') do echo "%%D\%%F" "%%~nxD%%~xF")
    for /r %%F in (*) do (for /f "delims=" %%D in ("%%~dpF.") do ren "%%F" "%%~nxD%%~xF")

    REM Copy
    for /r "F:\All_Models\Models" %%i in (*.cgf) do copy "%%i" "F:\All_Models\Catalog"

我的目录文件夹现在将列出所有名为:SubFolder.cgf 的文件(不够详细) 该脚本还更改了原始文件夹中的所有文件名,这些文件名仍应命名为:model.cgf

如果能帮助我改进我的脚本,我将不胜感激:)

谢谢!

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION 
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
 'dir /s /b /a-d "%sourcedir%\models\model.cfg" '
 ) DO (
  SET "destination=%%~dpa"
  SET "destination=!destination:*\models\=!"
  SET "destination=!destination:\=_!"
 ECHO MOVE "%%a" "%sourcedir%\catalog\!destination:~0,-1!.cfg"
)
GOTO :EOF

您需要更改 sourcedir 的设置以适合您的情况。该列表使用适合我的系统的设置。

for 循环将每个 model.cfg 文件位置依次传送到 %%a。驱动器和路径分配给 destination,然后 models\ 之前的部分被 nothing 替换,反斜杠转换为下划线。然后构建实际目标,记住 ~dp 包含最后一个反斜杠(现在是下划线),因此使用 0,-1 子字符串将其删除。

请注意,这使用了延迟扩展,因此如果树包含的目录包括 ! 除了通常的嫌疑人之外,请做好娱乐和游戏的准备。

所需的 MOVE 命令仅 ECHOed 用于测试目的。 确认命令正确后,将ECHO MOVE 更改为MOVE 以实际移动文件。附加 >nul 以禁止报告消息(例如 1 file moved

这是一种解决方案:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

for %%I in ("%~dp0..\Models") do set "ModelsPath=%%~fI\"
for %%I in ("%~dp0..\Catalog") do set "CatalogPath=%%~fI"

for /F "delims=" %%I in ('dir "%ModelsPath%model.cfg" /A-D /B /S 2^>nul') do (
    set "FilePath=%%~dpI"
    set "FilePath=!FilePath:~0,-1!"
    set "NewFileName=!FilePath:%ModelsPath%=!"
    set "NewFileName=!NewFileName:\=_!%%~xI"
    ren "%%I" "!NewFileName!"
    if exist "%%~dpI!NewFileName!" (
        %SystemRoot%\System32\robocopy.exe "!FilePath!" "%CatalogPath%" "!NewFileName!" /XO /R:3 /W:5 /NS /NC /NFL /NDL /NP /NJH /NJS >nul
        if not errorlevel 1 echo Ignored older "%%I"
        ren "%%~dpI!NewFileName!" "%%~nxI"
    ) else echo Failed to rename "%%I"
)

endlocal

此批处理文件仅在没有文件夹或文件名包含 ! 并且 Models 文件夹的文件夹路径不包含任何地方 =.

时才有效

批处理文件首先从批处理文件路径中确定目录CatalogModels的绝对路径。请注意,环境变量 ModelsPath 保存文件夹 Models 的绝对路径,末尾有反斜杠,而 CatalogPath 末尾没有反斜杠。

然后带有选项 /F 的命令 FOR 在以 %ComSpec% /c 和在 ' 中指定的命令行开始的单独命令进程中运行。 ..' 作为后台命令 DIR 的附加参数。所以执行是 C:\Windows 是 Windows 目录, C:\Temp\Models\ 被分配给 ModelsPath:

C:\Windows\System32\cmd.exe /c dir "C:\Temp\Models\model.cfg" /A-D /B /S 2>nul

DIR 搜索

  • 文件夹C:\Temp\Models
  • 及其所有子文件夹,因为选项 /S
  • 对于名称为 model.cfg
  • 的文件
  • 由于 /A-D 而排除目录,这些目录偶然也被命名为 model.cfg
  • 并使用完全限定文件名输出找到的文件,即路径 + 文件名 + 文件扩展名,因为选项 /B/S.

DIR 处理启动命令进程的 STDOUT 的输出被 FOR 捕获并在启动后逐行处理 cmd.exe 自行终止。

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

FOR 选项 delims= 禁用 FOR 的行拆分行为,以获取每个完全限定的文件名始终完全分配给指定的循环变量 I.

首先确定当前model.cfg的文件路径,末尾没有反斜杠。

然后 Models 删除开头的路径(末尾有反斜杠),并将剩余路径分配给环境变量 NewFileName。接下来,剩余路径中的所有反斜杠都将替换为下划线。

ROBOCOPY 用于将此文件及其新文件名复制到文件夹 Catalog 之前,当前 model.cfg 已重命名为新文件名目标文件夹中不存在源文件,或者源文件比目标文件夹中同名文件新。

如果文件由应用程序打开,并且文件共享权限设置为拒绝在应用程序打开时重命名文件,则无法重命名文件。这就是附加 IF 条件的原因。

最后,当前 model.cfg 在其源文件夹中重命名回此文件名。

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

  • echo /?
  • endlocal /?
  • dir /?
  • for /?
  • if /?
  • ren /?
  • robocopy /?
  • set /?
  • setlocal /?

另见 SS64:

在看到 Magoo 的回答之前,我已经开始创建脚本了。

我的答案几乎是一样的,除了:

  • 所有路径计算都是相对于脚本的位置,因此无需设置源目录或目标目录
  • 我以不同的方式消除了前导路径前缀。如果前导路径包含 = 字符(不太可能是问题)
  • ,我的方法将失败
  • 我复制文件而不是移动文件,我认为这是 OP 的意图
  • 我隐藏复制的结果并 ECHO 复制文件的结果名称
  • 我将所有需要的路径转移到变量,然后打开和关闭延迟扩展,以保护任何路径中可能存在的任何 !
@echo off
setlocal disableDelayedExpansion
for /f "delims=" %%F in (
  'dir /b /s "%~dp0Models\Model.cfg"'
) do (
  set "old=%%F"
  set "new=%%~pF"
  setlocal enableDelayedExpansion
  set "new=!new:%~p0Models\=!"
  set "new=!new:\=_!"
  set "new=!new:~0,-1!.cfg"
  echo !new!
  copy "!old!" "%~dp0Catalog\!new!" >nul
  endlocal
)

我懒得尝试只复制新文件。目录中的现有文件将被简单地覆盖。要正确确定文件是否已更改,您需要使用 FC,这似乎是浪费时间和精力。

这是一种可能的方式:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_SOURCE=%~dp0Models"
set "_TARGET=%~dp0Catalog"
set "_MASK=model*.cfg"
set "_CHAR=_"

rem // Change into source directory:
pushd "%_SOURCE%" && (
    rem /* List matching files by `xcopy /L` and
    rem    capture the result by `for /F`: */
    for /F "tokens=2 delims=:" %%F in ('
        xcopy /L /S /I "%_MASK%" "%TEMP%"
    ') do (
        rem // Store relative path of currently iterated item:
        set "ITEM=%%F"
        setlocal EnableDelayedExpansion
        rem // Split off file name from relative path:
        set "PAR=!ITEM!|" & set "PAR=!PAR:\%%~nxF|=!"
        rem // Build new name and process current item:
        copy /Y "!ITEM!" "!_TARGET!\!PAR:\=%_CHAR%!%%~xF"
        endlocal
    )
    popd
)

endlocal
exit /B

这种方法利用了以下事实:xcopy returns the source paths relative to the root, /S recurses sub-directories and /L prevents copying but just lists files that would be copied without. A for /F loop 捕获相对路径列表,获取父目录,将每个路径分隔符 \ 替换为 _ 以构建目标文件名称,然后将每个文件复制到目的地。已经存在的文件在没有提示的情况下被覆盖;要确认,只需将 /Y 替换为 /-Y。当然,要移动文件而不是复制,请将 copy 替换为 move