使用部分名称创建文件夹和移动文件的批处理脚本

Posted

技术标签:

【中文标题】使用部分名称创建文件夹和移动文件的批处理脚本【英文标题】:Batch script to create folders and move files using part of their name 【发布时间】:2021-07-01 07:19:13 【问题描述】:

我有一个文件列表:

Pcat2.zip
Pcat3.zip
Pcat22.zip
PcatGig10.zip
PcatGig21.zip
Pcolt2ned.zip
PColt3ned.zip
PColt10ned.zip
PColtI-1.zip
PColtIII-6.zip
PcoltIII-11.zip
PcoltIII-18.zip
PcoltIV-2.zip
PetPap25.zip
Pier4.zip
Pier16.zip
ProvSegrIV-4.zip
ProvSegrIII-1.zip
AttFIII-29.zip
AttFlI-5.zip
AttFlII-20.zip
AttFlVI-18.zip

我尝试使用根据文件名中的键字符串创建目录的脚本,并将文件移动到其中,如下所示:

|
+---Pcat
|      Pcat2.zip
|      Pcat3.zip
|      Pcat22.zip
|
+---PcatGig
|      PcatGig10.zip
|      PcatGig21.zip
|
+---Pcolt
|      Pcolt2ned.zip
|      PColt3ned.zip
|      PColt10ned.zip
|      PColtI-1.zip
|      PColtIII-6.zip
|      PcoltIII-11.zip
|      PcoltIII-18.zip
|      PcoltIV-2.zip
|
+---PetPap
|      PetPap25.zip
|
+---Pier
|      Pier4.zip
|      Pier16.zip
|
+---ProvSegr
|      ProvSegrIV-4.zip
|      ProvSegrIII-1.zip
|
+---AttF
|      AttFIII-29.zip
|
\---AttFl
       AttFlI-5.zip
       AttFlII-20.zip
       AttFlVI-18.zip

但是,我也想去掉十进制和类似罗马数字的数字

I-5
III-6
VI-18
VI-18
III-29
...

为了实现这一点,我尝试了这个脚本,但它不起作用。 我看看这里 Implement Regex in batch or powershell script to generate folders and move files in relative folders ordered by key string in file name

@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

脚本需要包含所有要处理的文件的目录作为第一个命令行参数。创建的子目录放置在其中。可选的第二个命令行参数定义文件名模式以过滤某些文件类型/名称。假设保存为D:\Script\build-folder-hierarchy.bat,文件在D:\Data,你只想处理*.zip文件,运行如下:

"C:\Script\build-folder-hierarchy.bat" "C:\Data" "*.zip"

【问题讨论】:

我已尝试改进您的问题帖子,但在此过程中,我注意到了一个异常情况,我无法准确地将其置于您的问题中。关于剥离名称以及十进制和罗马数字的部分。首先是许多罗马数字,例如IVXLCDM 是字母,已经作为字符串组件包含在您的文件名中,其次,如果这些被删除,您将遇到多个有问题的名称冲突。请澄清它应该是什么意思,以及你目前在你的代码中做了什么来迎合它。此外,“它不起作用”也不是一个有用的解释。 @Compo 是的,你是对的。我将“罗马数字”替换为“类似罗马的数字”,因为在这里您不能将其视为字母而是字母数字字母,因为在文件名中您可以找到I-5III-6VI-18III-29。 . ... 您的问题特别提到了剥离,而不是替换,您的代码似乎也没有这样做。本网站不提供您自己未尝试过的代码,它不是免费的脚本/编码服务。在许多地方,您可以与某人签订服务合同,为您提供编码和脚本解决方案,根据指南,这不是其中之一。还请调试您的脚本,正如我已经提到的,您需要解释脚本的哪一部分“不起作用”,即什么没有按预期工作,什么是/没有发生,等等。跨度> 另外,您还没有回答我的问题,脚本如何知道PColtIProvSegrIV 是字符串,还是与罗马数字连接的字符串?代码是否需要识别一个或多个字母字符的每个可能序列,这些字母字符可以被理解为有效的罗马数字本身?还是您希望它具有某种类似人类的 AI 能力? 您还需要提前明确定义您希望创建的目录名称,因为必须有一种机制来属性PcatGig字符串文件,之前Pcat字符串文件,否则如果先解析,前者将被放置到后者的位置! 【参考方案1】:

这是一个完成您需要的任务的脚本(请参阅所有解释性 rem 备注):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_ROOT=%~dp0TEST"      & rem // (target directory containing files to process)
set "_MASK=*.zip"          & rem // (pattern to match the files against)
set "_SEP=-"               & rem // (separator between roman and decimal numbers)
set "_ROMAN=I V X L C D M" & rem // (characters that build up roman numbers)

rem // Change into the target directory:
pushd "%_ROOT%" && (
    rem // Loop through all matching files:
    for /F "delims= eol=|" %%F in ('dir /B /A:-D-H-S "%_MASK%"') do (
        rem // Store full file name and base name in variables:
        set "FILE=%%F" & set "NAME=%%~nF"
        rem // Toggle delayed expansion to avoid trouble with `!`:
        setlocal EnableDelayedExpansion
        rem /* Split off (first) group of decimal numerals and everything after from
        rem    base name: */
        for /F "delims=0123456789 eol=0" %%E in ("_!NAME!") do (
            endlocal
            rem // Store resulting file name prefix in variable:
            set "PREF=%%E"
            setlocal EnableDelayedExpansion
            rem /* Check whether last character is the predefined separator character
            rem    between roman and decimal numbers and remove it in case; afterwards,
            rem    split off all characters that may build up a roman number: */
            set "PREF=!PREF:*_=!"
            if defined PREF if "!PREF:~-1!"=="!_SEP!" (
                set "PREF=!PREF:~,-1!"
                call :ROMAN PREF PREF
            )
        )
        rem // Create sub-directory named like the retrieved file name prefix:
        2> nul md "!PREF!"
        rem // Move the currently iterated file into the sub-directory (no overwrite):
        if not exist "!PREF!\!FILE!" > nul move /Y "!FILE!" "!PREF!"
        endlocal
    )
    rem // Return from the target directory:
    popd
)

endlocal
exit /B


:ROMAN
    rem // Remove roman number from the end of a provided string:
    set "#STR=%~2"
    set "#RTN=%~1"
    set "%#RTN%=!%#STR%!"
:ROMAN_LOOP
    if defined %#RTN% (
        rem /* Check whether the last character of the string is a valid roman numeral
        rem    and split it off in case: */
        set "FLAG=" & for %%R in (!_ROMAN! !_ROMAN! !_ROMAN!) do (
            if "!%#RTN%:~-1!"=="%%R" set "FLAG=#"
        )
        if defined FLAG set "%#RTN%=!%#RTN%:~,-1!" & goto :ROMAN_LOOP
    )
    exit /B

实现如下方法:

从文件名的第一个数字部分开始拆分所有内容; 检查余数是否以- 结尾,它将您的示例文件中的罗马数字与十进制数字分开; 如果它确实删除它并从末尾删除最长的序列,该序列由可能构建罗马数字的字符组成,而不检查它是否真的是有效的;

【讨论】:

非常感谢。我只将set "_ROOT=%~dp0TEST" 更改为设置"_ROOT=%C:\Users\Administrator\Test\"【参考方案2】:

以下仅使用 Windows 命令处理器的内部命令的非常低效的批处理文件适用于名称符合以下条件的文件:

    文件名中的罗马数字在IXXXIX(1 到49)的范围内,罗马数字留给连字符和十进制数字或留给文件扩展名。在应用替换时,命令 SET 会解释罗马数字不区分大小写。 文件名中09 范围内的第一个 十进制数字被解释为分隔子字符串以用作文件夹名称的字符,其余文件名部分将被忽略文件夹名称。 文件夹名称忽略文件名开头的十进制数字。 作为第二个参数传递给批处理文件的通配符模式应该以文件扩展名结尾。像*.* 这样的东西只会处理文件扩展名为第一个文件的文件,这些文件由文件系统返回到处理批处理文件的命令进程,而不是源文件夹中的所有文件。

批处理文件代码为:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "SourceFolder=%~1"
if not defined SourceFolder set "SourceFolder=%CD%"
for %%I in ("%SourceFolder%") do set "SourceFolder=%%~fI"
if not "%SourceFolder:~-1%" == "\" set "SourceFolder=%SourceFolder%\"

set "FilePattern=%~2"
if not defined FilePattern set "FilePattern=*.zip"
for /F "tokens=* delims=?* eol=|" %%I in ("%FilePattern%") do if "%FilePattern%" == "%%I" if "%FilePattern:~0,1%" == "." (set "FilePattern=*%FilePattern%") else set "FilePattern=*.%FilePattern%"

for %%I in ("%SourceFolder%%FilePattern%") do set "FileExtension=%%~xI" & goto ProcessFiles
setlocal EnableDelayedExpansion
echo ERROR: There are no files matching the pattern !FilePattern! in folder:
echo        "!SourceFolder!"
endlocal
echo/
pause
goto :EOF

:ProcessFiles
pushd "%SourceFolder%" || goto :EOF
for /F "eol=| delims=" %%I in ('dir "%FilePattern%" /A-D /B /O-N 2^>nul') do (
    set "FileName=%%I"
    set "FolderName=%%I"
    setlocal EnableDelayedExpansion
    for %%J in (XXXIX XXXVIII XXXVII XXXVI XXXV XXXIV XXXIII XXXII XXXI XXX) do (
        for %%K in (0 1 2 3 4 5 6 7 8 9) do set "FolderName=!FolderName:%%J-%%K=0!"
        set "FolderName=!FolderName:%%J%FileExtension%=0%FileExtension%!"
    )
    for %%J in (XXIX XXVIII XXVII XXVI XXV XXIV XXIII XXII XXI XX) do (
        for %%K in (0 1 2 3 4 5 6 7 8 9) do set "FolderName=!FolderName:%%J-%%K=0!"
        set "FolderName=!FolderName:%%J%FileExtension%=0%FileExtension%!"
    )
    for %%J in (XIX XVIII XVII XVI XV XIV XIII XII XI X) do (
        for %%K in (0 1 2 3 4 5 6 7 8 9) do set "FolderName=!FolderName:%%J-%%K=0!"
        set "FolderName=!FolderName:%%J%FileExtension%=0%FileExtension%!"
    )
    for %%J in (IX VIII VII VI V IV III II I) do (
        for %%K in (0 1 2 3 4 5 6 7 8 9) do set "FolderName=!FolderName:%%J-%%K=0!"
        set "FolderName=!FolderName:%%J%FileExtension%=0%FileExtension%!"
    )
    for /F "eol=| delims=0123456789" %%J in ("!FolderName!") do (
        endlocal
        set "FolderName=%%J"
        setlocal EnableDelayedExpansion
    )
    if "!FileName!" == "!FolderName!" (
        endlocal
        set "FolderName=%%~nI"
        if not defined FolderName set "FolderName=%%~xI"
        setlocal EnableDelayedExpansion
    )
    md "!FolderName!" 2>nul
    move /Y "!FileName!" "!FolderName!\" >nul
    endlocal
)
popd
endlocal

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

call /? ... 解释了如何像 %~1%~2 那样引用批处理文件参数。 dir /? echo /? endlocal /? for /? goto /? if /? md /? move /? pause /? popd /? pushd /? set /? setlocal /?

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

【讨论】:

以上是关于使用部分名称创建文件夹和移动文件的批处理脚本的主要内容,如果未能解决你的问题,请参考以下文章

TiOps脚本库如何使用

如何制作一个批处理脚本来创建指定数量的具有随机名称和指定扩展名的文件? [复制]

使用Linux命令行

Linux系统文件查找

在 FOR /R LOOP 批处理命令期间重新创建子文件夹?

批处理实现mysql的备份