【问题标题】:Fix moving and consolidating folders when use batch file process修复使用批处理文件处理时移动和合并文件夹
【发布时间】:2019-12-16 20:46:31
【问题描述】:

我有这个代码

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "SPLITCHAR=-"  & rem // (a single character to split the file names)
set "SEARCHSTR=_"  & rem // (a certain string to be replaced by another)
set "REPLACSTR= "  & rem // (a string to replace all found search strings)
set "OVERWRITE="   & rem // (set to non-empty value to force overwriting)

rem // Get file location and pattern from command line arguments:
set "LOCATION=%~1" & rem // (directory to move the processed files into)
set "PATTERNS=%~2" & rem // (file pattern; match all files if empty)

rem /* Prepare overwrite flag (if defined, set to character forbidden
rem    in file names; this affects later check for file existence): */
if defined OVERWRITE set "OVERWRITE=|"
rem // Continue only if target location is given:
if defined LOCATION (
    rem // Create target location (surpress error if it already exists):
    2> nul md "%LOCATION%"
    rem /* Loop through all files matching the given pattern
    rem    in the current working directory: */
    for /F "eol=| delims=" %%F in ('dir /B "%PATTERNS%"') do (
        rem // Process each file in a sub-routine:
        call :PROCESS "%%F" "%LOCATION%" "%SPLITCHAR%" "%SEARCHSTR%" "%REPLACSTR%"
    )
)

endlocal
exit /B


:PROCESS
rem // Retrieve first argument of sub-routine:
set "FILE=%~1"
rem // Split name at (first) split character and get portion in front:
for /F "delims=%~3" %%E in ("%~1") do (
    rem // Append a split character to partial name:
    set "FOLDER=%%E%~3"
)
setlocal EnableDelayedExpansion
rem // Right-trim partial name:
if not "%~4"=="" set "FOLDER=!FOLDER:%~4%~3=!"
set "FOLDER=!FOLDER:%~3=!"
rem /* Check whether partial name is not empty
rem    (could happen if name began with split character): */
if defined FOLDER (
    rem // Replace every search string with another:
    if not "%~4"=="" set "FOLDER=!FOLDER:%~4=%~5!"
    rem // Create sub-directory (surpress error if it already exists):
    2> nul md "%~2\!FOLDER!"
    rem /* Check if target file already exists; if overwrite flag is
    rem    set (to an invalid character), the target cannot exist: */
    if not exist "%~2\!FOLDER!\!FILE!%OVERWRITE%" (
        rem // Move file finally (surpress `1 file(s) moved.` message):
        1> nul move /Y "!FILE!" "%~2\!FOLDER!"
    )
)
endlocal
exit /B

我以这种方式使用命令提示符来创建文件夹并将其中的文件从文件夹 1 移动到文件夹 2

cd /D "C:\Users\Administrator\Downloads\"

"C:\Users\Administrator\Downloads\test1\build-folder-hierarchy.bat" "C:\Users\Administrator\Downloads\test2" "*.mkv"

什么问题?

但我想从文件移动中获得文件夹合并,而不是从文件中生成相同数量的文件夹

The.Race.Corsa.Mortale.2019.S1E02.Episodio2.HDTV.AAC.iTA.X264-ARSENAL.mkv
The.Race.Corsa.Mortale.2019.S1E01.Episodio1.HDTV.AAC.iTA.X264-ARSENAL.mkv
The.Feed.1x05.Episodio.5.ITA.DLMux.x264-UBi.mkv
The.Feed.1x04.Episodio.4.ITA.DLMux.x264-UBi.mkv
The.Feed.1x03.Episodio.3.ITA.DLMux.x264-UBi.mkv
The.Feed.1x02.Episodio.2.ITA.DLMux.x264-UBi.mkv
The.Feed.1x01.Episodio.1.ITA.DLMux.x264-UBi.mkv
Swamp.Thing.1x10.La.Resa.Dei.Conti.ITA.DLMux.x264-UBi.mkv
Volevo.Fare.La.Rockstar.1x11.Confusione.ITA.WEBRip.x264-UBi.mkv
Volevo.Fare.La.Rockstar.1x07.Tabu.ITA.WEBRip.x264-UBi.mkv
Volevo.Fare.La.Rockstar.1x01.Buon.Compleanno.Olly.ITA.WEBRip.x264-UBi.mkv
Volevo Fare La Rockstar 1x12 La Tribu Ita Webrip x264-Ubi.mkv
Virgin.River.1x10.Finali.inattesi.720p.iTA.AAC.DLRip.x265.-.T7.mkv
Virgin.River.1x07.A.dire.il.vero.720p.iTA.AAC.DLRip.x265.-.T7.mkv
Virgin.River.1x04.Un.Cuore.Ferito.iTA.AC3.WEBMux.x264-ADE.CreW.mkv
Virgin.River.1x01.La.Vita.Continua.iTA.AC3.WEBMux.x264-ADE.CreW.mkv
Tre.Giorni.Di.Natale.1x03.Episodio.3.iTA.AC3.WEBMux.x264-ADE.CreW.mkv
Tre.Giorni.Di.Natale.1x01.Episodio.1.iTA.AC3.WEBMux.x264-ADE.CreW.mkv

但是我想用这种方式获取文件夹和移动文件

├─The Race Corsa Mortale [folder]
│ ├─The.Feed.1x05.Episodio.5.ITA.DLMux.x264-UBi [file]
│ ├─The.Feed.1x04.Episodio.4.ITA.DLMux.x264-UBi [file]
  └─ ....
├─Virgin River [folder]
│ └─Virgin.River.1x07.A.dire.il.vero.720p.iTA.AAC.DLRip.x265 [file]
:

我也尝试使用这个批处理脚本,但它不起作用:我通过资源管理器点击,但就像被禁用(我使用 Windows Server 2012)

@echo off
setlocal EnableDelayedExpansion

rem Change current directory to the one where this .bat file is located
cd "%~P0"

set "digits=0123456789"

rem Process all *.mkv files
for %%f in (*.mkv) do (

   rem Get the folder name of this file
   call :getFolder "%%f"

   rem If this file have a properly formatted name: "headS##Etail"
   if defined folder (
      rem Move the file to such folder
      if not exist "!folder!" md "!folder!"
      move "%%f" "!folder!"
   )

)
goto :EOF


:getFolder file

set "folder="
set "file=%~1"
set "head="
set "tail=%file%"

:next
   for /F "delims=%digits%" %%a in ("%tail%") do set "head=%head%%%a"
   set "tail=!file:*%head%=!"
   if not defined tail exit /B
   if /I "%head:~-1%" equ "S" goto found
   :digit
      if "!digits:%tail:~0,1%=!" equ "%digits%" goto noDigit
      set "head=%head%%tail:~0,1%"
      set "tail=%tail:~1%"
   goto digit
   :noDigit
goto next

:found
for /F "delims=Ee" %%a in ("%tail%") do set "folder=%head%%%a"
exit /B

我也接受 Powershell 解决方案

编辑:我需要的文件名部分是S#E##、#x##.#x##、.#x#、.#x## 和类似文件之前的部分

【问题讨论】:

  • 为什么这个标签是PowerShell?
  • @Theo,他说他“也将接受 powershell 解决方案”,我相信这就是原因。
  • 在 Super Sonic,问题是您无法分辨文件名的哪一部分是您要用来命名目录的部分,以及您要命名文件的部分是哪一部分.最好的选择可能是使用季节的数字,但你可能有带数字的名字。第二个脚本正在尝试执行此方法。
  • @BenPersonick 啊,我的错。没有读到最后一行..
  • @BenPersonick 我需要的文件名部分是S#E##、.#x##、.#x#、.#x## 之前的部分

标签: windows powershell batch-file cmd


【解决方案1】:

我可能会使用以下脚本(请查阅所有解释性rem 备注):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_ROOT=%~1"    & rem /* (target directory; `.` is current working directory, `%~dp0.` is
                     rem     parent of this script, `%~1` is first command line argument) */
set _MASKS="*.mkv" & rem // (space-separated list of quoted file patterns)
set _SEPS=" " "."  & rem // (space-separated list of quoted separators)
rem /* Specify multiple `findstr` search strings, including the prefix `/C:`, as you would
rem    directly state them at the `findstr` command, which are used to match the particular
rem    sub-strings of the file names that are used to find the part where to split them at
rem    and to derive the name of the sub-directory where to move the respective file to: */
set _FILTERS=/C:"^S[0123456789][0123456789]*E[0123456789][0123456789]*$" ^
              /C:"^[0123456789][0123456789]*x[0123456789][0123456789]*$"

rem // Change into root directory:
pushd "%_ROOT%" && (
    rem // Loop through all matching files:
    for /F "delims= eol=|" %%F in ('dir /B /A:-D-H-S %%_MASKS%%') do (
        rem // Store current file name and extension, initialise some auxiliary variables:
        set "NAME=%%~nF" & set "EXT=%%~xF" & set "SDIR= " & set "FLAG=#"
        rem // Toggle delayed expansion to avoid trouble with `!` (also later on):
        setlocal EnableDelayedExpansion
        rem // Replace all predefined separators by spaces:
        for %%S in (!_SEPS!) do set "NAME=!NAME:%%~S= !"
        rem // Loop through all space-separated (quoted) items of the file name:
        for %%I in ("!NAME: =" "!") do (
            rem // Skip the loop body when a sub-string has already been found before:
            if defined FLAG (
                rem // Store current portion of the file name:
                endlocal & set "ITEM=%%~I"
                rem // Use `findstr` to match against the predefined sub-strings:
                cmd /V /C echo(!ITEM!| > nul findstr /R /I %_FILTERS% && (
                    rem // Match encountered, hence skip this and the remaining items:
                    set "FLAG="
                ) || (
                    rem /* No match found, so append the current item to the name of the
                    rem    sub-directory where the file is supposed to be moved then: */
                    setlocal EnableDelayedExpansion
                    for /F "delims=" %%E in ("!SDIR!!ITEM! ") do (
                        endlocal & set "SDIR=%%E"
                    )
                )
                setlocal EnableDelayedExpansion
            )
        )
        rem // Process only file naes where sub-directory names could be derived from:
        if not defined FLAG if not "!SDIR:~1,-1!"=="" (
            rem // Create sub-directory, if not yet existing:
            ECHO 2> nul mkdir "!SDIR:~1,-1!"
            rem // Move current file into the sub-directory (but do not overwrite in case):
            ECHO if not exist "!SDIR:~1,-1!\!NAME!!EXT!" > nul move "!NAME!!EXT!" "!SDIR:~1,-1!\"
        )
        endlocal
    )
    popd
)

endlocal
exit /B

假设脚本名为consolidate.bat,目标目录为%UserProfile%\Downloads,调用脚本如下:

consolidate.bat "%UserProfile%\Downloads"

测试正确的输出后,删除mkdir 和move 命令前面的大写ECHO 命令!

【讨论】:

  • 我测试了您的脚本,但它会正确创建文件夹,但文件不会在相应文件夹中移动。我将ECHO 2> nul mkdir 替换为2> nul mkdir "!SDIR:~1,-1!",将ECHO if not exist "!NAME!!EXT!" move 替换为if not exist "!NAME!!EXT!" move。脚本名为 consolidate.bat,位于 C:\Users\Administrator\Downloads\test1 中。文件在脚本的同一路径中。
  • 在命令提示符中输入cd C:\Users\Administrator\Downloads\test1,然后输入consolidate.bat "C:\Users\Administrator\Downloads\test1":文件夹已创建,但文件不会在其中移动。 S#E##、.#x##、.#x#、.#x## 是分隔符的示例,例如 The.Feed.1x04..、The.Feed.S1x04...、The.Feed.S1x04.、、The. Feed.S1E04...,The.Feed.1x4.
  • 糟糕,抱歉,出了点小错误,if exist 看错了位置……
  • 对于文件名为 Pippi Calze Lunghe Ep 01 - Villa Villa Colle 我尝试将过滤器设置为 /C:"^Ep [0123456789][0123456789]*$" ^ 但不起作用。如何包含空格?
  • 空格用来分隔文件名的各个部分,所以Ep和01是分开的部分;但是,我会简单地将/C:"^Ep$" 指定为搜索表达式,因为Ep 无论如何都不应该出现在标题部分,我猜/希望...
【解决方案2】:

假设我们可以在名称中的 Digits 上拆分文件名,这应该是必要的。

我暂停一下,检查输出并确保在单击输入之前它为您提供了正确的信息。

@(SETLOCAL EnableDelayedExpansion
  ECHO OFF
  REM SET "_SrcFolder=C:\Users\Administrator\Downloads\test1"
  REM SET "_DstFolder=C:\Users\Administrator\Downloads\test2"
  REM SET "FileGlob=*.mkv"
  SET "_SrcFolder=C:\Admin"
  SET "_DstFolder=C:\Admin\test2"
  SET "FileGlob=*.txt"
)
CALL :Main

( ENDLOCAL
  EXIT /B
)

:Main
  FOR %%A IN (
    "%_SrcFolder%\%FileGlob%"
  ) DO (
    CALL :Process "%%~nA" "%%~xA" "%%~fA"
  )
GOTO :EOF

:Process
  SET "_OLDName=%~1"
  FOR /F "Tokens=1 Delims=0123456789" %%a IN ("%~1") DO (
    SET "_NewFolder=%%a"
  )
  SET "_NewName=!_OLDName:%_NewFolder%=!"
  ECHO._OLDName=%_OLDName%.%~2
  ECHO._NewFolder=%_NewFolder%
  ECHO._NewName=%_NewName%.%~2
  REM pause
  ECHO.
  ECHO.  We will now do the following if you press any key:
  ECHO. MD "%_DstFolder%\%_NewFolder%\"
  ECHO. COPY "%~3" "%_DstFolder%\%_NewFolder%\%_NewName%%~2"
  ECHO.

  PAUSE

  MD "%_DstFolder%\%_NewFolder%\"
  COPY "%~3" "%_DstFolder%\%_NewFolder%\%_NewName%%~2"
GOTO :EOF

【讨论】:

  • 执行此脚本后没有任何反应,只出现 cmd 但在 test2 文件夹中我什么也看不到 i.imgur.com/iRT6Ttk.png
  • 你的代码只是一个片段,不是完整的?我尝试执行您的代码,但没有任何反应,文件未在文件夹中移动
  • @SuperSonic 里面的一些错误我不得不赶去开会,把它全部修好。并更新
  • @SuperSonic 你从未测试过我昨天发布的更新版本,它解决了一些小问题。
  • 您的脚本不起作用。我创建了一个 .bat,复制您的代码并保存。我单击它,但没有移动文件,也没有创建文件夹。文件在脚本的同一路径中
猜你喜欢
  • 2015-09-28
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
  • 1970-01-01
  • 2018-02-24
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多