【问题标题】:Windows bat file script to format source date用于格式化源日期的 Windows bat 文件脚本
【发布时间】:2021-11-21 16:15:07
【问题描述】:

我的 Windows 服务器文件夹中有以下格式的文件:

C:\ABC\abcdef - ggggg hhhhhh iiii jj xxxx kkk - pp qq Sep 21, 2021.txt

我需要检查文件夹中是否存在文件并从文件名中获取日期。然后将文件移动到另一个文件夹,并使用新文件名并以MMddyyyy 格式附加源文件日期。

文件应该移动到目录:

C:\ABC\DEF

姓名:

abcdef_xxxx_09212021.txt

我需要帮助来编写 Windows 批处理脚本以从源文件名中获取日期并使用在新文件名中重新格式化的日期来移动文件。

我知道文件移动和重命名,但需要帮助来保存日期值,然后将格式从 MMM dd, yyyy 更改为 MMddyyyy 并附加新文件名,如下面的命令行:

IF EXIST "C:\ABC\abcdef - ggggg hhhhhh iiii jj xxxx kkk -*.txt" move /Y "C:\ABC\abcdef - ggggg hhhhhh iiii jj xxxx kkk -*.txt" C:\ABC\DEF\abcdef_xxxx_.txt

【问题讨论】:

    标签: windows batch-file cmd


    【解决方案1】:

    重命名文件的文件移动任务,文件名中的日期重新格式化,可以使用以下批处理文件完成:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    set "BaseFolder=C:\ABC"
    set "!Jan=01"
    set "!Feb=02"
    set "!Mar=03"
    set "!Apr=04"
    set "!May=05"
    set "!Jun=06"
    set "!Jul=07"
    set "!Aug=08"
    set "!Sep=09"
    set "!Oct=10"
    set "!Nov=11"
    set "!Dec=12"
    for /F "eol=| delims=" %%G in ('dir "%BaseFolder%\* - * * * * * * - * * * *, *.txt" /A-D-H /B 2^>nul') do for /F "eol=| tokens=1,7,12-14 delims=, " %%H in ("%%G") do for /F "tokens=2 delims==" %%# in ('set !%%J 2^>nul') do md "%BaseFolder%\DEF" 2>nul & move /Y "%BaseFolder%\%%G" "%BaseFolder%\DEF\%%H_%%I_%%#%%K%%L" >nul
    endlocal
    

    前两个命令行完全定义了所需的执行环境。

    接下来定义基本文件夹的路径,文本文件移动到分配给环境变量BaseFolder的基本文件夹的子文件夹中。

    接下来的命令行定义环境变量,名称缩写为月份,名称前面为!,其对应的两位数字为值。

    第一个 FOR 循环在后台再开始一个 cmd.exe 并带有选项 /c 和在 ' 中指定的命令行作为附加参数附加在带有通配符模式的基本文件夹中搜索要移动的文本文件。处理在后台启动的cmd.exe 进程的DIR 的输出(标准输出)由处理批处理文件的cmd.exe 捕获。捕获的行是通配符模式匹配的所有非隐藏*.txt 文件的文件名。

    第一个FOR循环在启动cmd.exe 后一行接一行地自行终止。默认情况下,首先使用普通空格和水平制表符作为字符串分隔符将每行拆分为子字符串。如果第一个子字符串以分号开头,则忽略该行以进行进一步处理。否则,第一个子字符串将分配给指定的循环变量,FOR 将执行接下来指定的命令。这里不需要这种行为,因为文件名肯定包含空格,甚至可能文件名以分号开头。出于这个原因,选项eol=| 用于将竖线定义为任何文件名都不能包含的行尾字符,选项delims= 用于指定字符串分隔符的空列表以禁用行拆分行为。因此,DIR 输出的不带路径的整个文件名被分配给循环变量G

    第二个FOR 处理带有文件扩展名的文件名字符串。这次需要使用逗号和普通空格作为字符串分隔符的拆分行为,因为以下子字符串很有趣:

    1. tokens=1 引用的第一个子字符串abcdef 分配给指定的循环变量H
    2. tokens= 选项字符串中用7 引用的第七个子字符串xxxx 根据ASCII table 分配给下一个循环变量I
    3. 第十二个子字符串 Sep 在分配给下一个循环变量 Jtokens= 选项字符串中用 12 引用。
    4. 分配给循环变量Ktokens=选项字符串中1214之间的-引用的第十三个子字符串21
    5. 分配给循环变量Ltokens=选项字符串中14引用的第十四个子字符串2021.txt

    文件名可能包含一个或多个!,因此最好避免使用需要使用延迟扩展的引用环境变量。每个工作日处理的文件也不多。

    因此,文件名中缩写月份的两位数字是通过使用另外一个FOR命令在后台运行cmd.exe和选项/c和命令来获得的SET 使用缩写月份作为环境变量名称,前面带有!。命令 SET 输出以处理后台命令的 STDOUT 处理以感叹号和缩写月份开头的所有环境变量。感叹号只是为了避免意外输出另一个名称也以当前月份缩写的三个字符开头的环境变量。环境变量名称通常不以感叹号开头,至少不是任何预定义的 Windows 环境变量。

    SET 输出变量名、等号和赋值。此输出由第三个 FOR 捕获并处理,这次使用等号作为分隔符,并分配给指定的循环变量#,仅等号后面的两位数。

    因此,现在所有子字符串都分配给了新文件名中所需的循环变量。

    因此,如果目标目录尚不存在,则执行命令 MD 创建目标目录,并通过从句柄 STDERR 重定向错误消息来抑制已存在的错误消息(标准错误)到设备 NUL

    Next 独立于命令 MD 命令的结果执行,命令 MOVE 移动基本文件夹中的文件,文件名和扩展名分配给第一个循环变量 G FOR 到目标文件夹,新文件名与第二个和第三个 FOR 确定的子字符串连接。通过将一个文件重定向到设备 NUL 来抑制成功移动一个文件的消息输出,同时将错误消息输出到控制台窗口。

    最后一个命令ENDLOCAL恢复初始环境。

    如果驱动器 C: 使用 NTFS 作为文件系统并且文本文件的文件名和基本文件夹路径中不包含感叹号,也可以使用以下批处理文件。

    @echo off
    setlocal EnableExtensions EnableDelayedExpansion
    set "BaseFolder=C:\ABC"
    set "Jan=01"
    set "Feb=02"
    set "Mar=03"
    set "Apr=04"
    set "May=05"
    set "Jun=06"
    set "Jul=07"
    set "Aug=08"
    set "Sep=09"
    set "Oct=10"
    set "Nov=11"
    set "Dec=12"
    md "%BaseFolder%\DEF" 2>nul 
    for %%G in ("%BaseFolder%\* - * * * * * * - * * * *, *.txt") do for /F "eol=| tokens=1,7,12-14 delims=, " %%H in ("%%~nxG") do move /Y "%%G" "%BaseFolder%\DEF\%%H_%%I_!%%J!%%K%%L" >nul
    endlocal
    

    此批处理文件执行得更快,因为没有其他cmd.exe 实例在后台启动,并且目标文件夹仅创建一次(希望成功)。它使用delayed expansion 来获取文件名中缩写月份的两位数,因此只需要两个 FOR 循环:

    1. 第一个 FOR 循环用于在此 FOR 循环的每次迭代中访问文件系统,以直接从文件中获取与通配符模式匹配的下一个文件名和完整路径系统。这适用于在 NTFS 驱动器上移动文件,但在 FAT32 或 exFAT 驱动器上可能无法正常工作,因为在 FAT 驱动器上,文件系统条目会随着文件的每次成功移动而改变。出于这个原因,通常最好先加载到cmd.exe 的内存中处理批处理文件,其名称与第一个批处理文件所做的通配符模式匹配的所有文件的列表,然后在内存中处理此列表文件系统条目在每次循环迭代中永久更改。
    2. 第二个 FOR 循环用于从新文件名中所需的文件名中获取子字符串,这与在第一个批处理文件中所做的完全一样。

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

    • dir /?
    • echo /?
    • endlocal /?
    • for /?
    • md /?
    • move /?
    • set /?
    • setlocal /?

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

    有关运算符& 的说明,另请参阅single line with multiple commands using Windows batch file

    【讨论】:

      【解决方案2】:

      只要您的文件始终具有相同的格式化空格,您就可以使用一对 For 循环来完成大部分工作。我使用的方法是您将有问题的文件,将它们拖过来并将它们放到批处理文件中,然后让'er rip。一次能够相对快速地处理大约 100 个文件,特别是如果它们都去同一个最终位置。我在下面的批处理解决方案中评论了很多,以帮助您了解我在做什么(所有“REM”行)

      以文件名“abcdef - ggggg hhhhhh iiii jj xxxx kkk - pp qq Sep 21, 2021.txt”为例,我们可以通过空格作为分隔符来解析名称。这为我们提供了我们需要完成的日期工作所需的令牌 12、13 和 14,以及来自文件名的“abcdef”和“xxxx”数据的令牌 1 和令牌 7,以创建“abcdef_xxxx_09212021.txt”

      @Echo Off
      setlocal enabledelayedexpansion enableextensions
      
      REM checking to make sure the 1st argument is NOT blank [meaning there is no file dropped onto the tool.]
      
      If "%~1"=="" (
        Echo Please drag and drop the file to parse onto the batch file
        pause
        GOTO :EOF
      )
      REM Returning from end of script if more than one file are dropped onto tool.
      :PostShift
      
      
      REM Establishing some variables for use later.
      REM "%~dp1" becomes the drive and path of arg 1 [the 1st file] which 
      REM is "C:\ABC\" [including the final \ in the path]
      REM this becomes the final destination - you would need to change
      REM this if it is different for each file, or you could specify that 
      REM with a prompt of some kind in between the shift cycle.
      
      set "destFolder=DEF"
      
      set "endDir=%~dp1!destFolder!"
      REM endDir=C:\ABC\DEF
      
      For /f "Tokens=1,7,12-14 Delims= " %%a in ("%~1") do (
        REM Token 1
        set "term1=%%a"
        REM Token 7
        set "term2=%%b"
        REM Token 12
        set "mth=%%c"
        REM Token 13
        set "day=%%d"
        REM Token 14
        set "yr=%%e"
      
        set mthCount=0
        REM For loop to determine the proper month.
        For %%M in (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec) do (
          REM increment mthCount up by 1 to start for Jan, then continue up until it sets the proper month
          set /a mthCount+=1
          REM One Liner to confirm the token for the month equals the month in the sequence of the 2nd For Loop
          REM then determines whether mthNumber [to be used in final filename] is defined.  
          REM If it is defined this will fail after being set, and finish out the loop with only the appropriate mth set.
      
          If "%%M"=="!mth!" if not defined mthNumber set mthNumber=!mthCount!
        )
      )
      
      REM Here we will ensure the "MM" format is always in use even w/ single digit months
      If !mth! LSS 10 set mth=0!mth!
      
      REM %~x1 provides the same extension as the file being processed so it doesn't have
      REM to be specified every time the file changes.
      Set "finalFile=!term1!_!term2!_!mth!!day!!yr!%~x1"
      
      REM This If is not necessary, but I saw you include this in your question, 
      REM and, for some it gives the nice "warm and fuzzy" feel to KNOW it exists.
      If Exist "%~1" (
        Echo I am going to attempt to move this file where you want it to be^^!
        REM Checking if new file already exists
        If Exist "!endDir!\!finalFile!" (
          Echo This file already exists.  Moving in as duplicate.
          Move "%~f1" "!endDir!\DUPLICATE_!finalFile!"
        ) ELSE (
          REM %~f1 is the fully qualified filename for the first file in the mix
          Move "%~f1" "!endDir!\!finalFile!"
        )
      )
      
      REM Now we check for other arguments [meaning, other files dropped on the tool at the same time]
      
      If "%~2"=="" (
        Echo No other files have been chosen to be parsed at this time.
        Echo Exiting . . .
        pause
        REM If you have nothing else after this portion of the Batch File, the following
        REM line is NOT needed.
        GOTO :EOF
      ) ELSE (
      
        REM Shifts "%2" [2nd file in list] to file one's spot to be parsed 
        REM without having 100's of iterations of code . . . Really, withouth SHIFT,
        REM you could only have 9 repetitions as only 9 arguments are accessible.
      
        shift /1
        REM Goes back to beginning of file
        GOTO :Postshift 
      )
      
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-12
        • 2020-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-13
        • 1970-01-01
        • 2010-11-14
        相关资源
        最近更新 更多