传输最新日期的文件名并删除早期日期的重复项

时间:2019-07-10 21:10:45

标签: date batch-file duplicates

当前,我必须将一堆Excel工作表上传到网络共享文件夹。每个文件都有,它们是在文件名的末尾附加创建的。然后,我必须删除较早的,仅保留最新的版本。

基本上看起来像这样...

之前:

apples 2019.07.01.xlsx
apples 2019.07.07.xlsx
oranges 2019.07.01.xlsx
bananas 2019.07.01.xlsx

之后:

apples 2019.07.07.xlsx
oranges 2019.07.01.xlsx
bananas 2019.07.01.xlsx

我偶然发现了一个可能的解决方案,该解决方案是创建一个以递归地遍历该文件夹并执行此操作。但是,我不确定从哪里开始。

我读了this other stackoverflow article,与我想做的很接近,但是我无法根据自己的需要进行调整。任何帮助将不胜感激。

Edit2:此代码对我有用:

@(
  SetLocal EnableDelayedExpansion
  ECHO OFF
  SET "_PathToCheck=Y:\T\DT"
  SET "_FileGlob=PLOG - * - ????.??.?? - *.xlsx"
  SET "_CurrentFile="
)

FOR /F "Tokens=1-2* Delims=-" %%A IN ('DIR /A-D /O-N /B "%_PathToCheck%\%_FileGlob%"') DO (
  IF /I "!_CurrentFile!" EQU "%%A-%%B" (
    ECHO.Deleting: "%_PathToCheck%\%%A-%%B-%%C"
    DEL /F /Q "%_PathToCheck%\%%A-%%B-%%C"
  ) ELSE (
    ECHO.
    ECHO.New File Found: "%%A-%%B"
    ECHO.-----------
    ECHO.Retaining: "%_PathToCheck%\%%A-%%B-%%C"
    SET "_CurrentFile=%%A-%%B"
  )
)

2 个答案:

答案 0 :(得分:1)

您可以使用与手动执行此操作相同的方法:查看文件列表,并在每次出现与先前名称相同的文件 时,删除先前的文件。一个...简单!是不是 ;)

@echo off
setlocal EnableDelayedExpansion

rem Initialize the "previous name"
set "lastName="
rem Process files in natural order, that is, the same order showed in the question
rem and set %%a to name and %%b to rest: date plus extension
for /F "tokens=1*" %%a in ('dir /B /A:-D /O:N *.xlsx') do (
   rem If previous name is not the same as current one
   if "!lastName!" neq "%%a" (
      rem Just update previous name and date
      set "lastName=%%a"
      set "lastDate=%%b"
   ) else (
      rem Remove the previous file
      ECHO del "!lastName! !lastDate!"
      rem and update the previous date
      set "lastDate=%%b"
   )
)

此解决方案假定名称和日期部分用精确地一个空格 ...

分隔

编辑在OP进行了一些令人困惑的更改之后,添加了新方法

@echo off
setlocal EnableDelayedExpansion

set "lastName="
for /F "delims=" %%a in ('dir /B /A:-D /O:N *.xlsx') do (
   set "currName="
   set "currFile="
   for %%b in (%%~Na) do (
      set "part=%%b"
      set "currFile=!currFile! !part!"
      if "!part:.=!" equ "!part!" set "currName=!currName! !part!"
   )
   if "!lastName!" neq "!currName!" (
      set "lastName=!currName!"
      set "lastFile=!currFile!"
   ) else (
      ECHO del "!lastFile:~1!.xlsx"
      set "lastFile=!currFile!"
   )
)

输入文件示例:

apples 2019.07.01.xlsx
apples 2019.07.07.xlsx
oranges 2019.07.01.xlsx
bananas 2019.07.01.xlsx
apples 2019.07.01 proof1.xlsx
apples 2019.07.07 proof1.xlsx
PLOG - Organic Valley - 2019.07.01 - (DAI) OG Cream Cheese.xlsx
PLOG - Organic Valley - 2019.07.07 - (DAI) OG Cream Cheese.xlsx
PLOG - Organic Valley - 2019.07.10 - (DAI) OG Cream Cheese.xlsx

输出:

del "apples 2019.07.01.xlsx"
del "apples 2019.07.01 proof1.xlsx"
del "PLOG - Organic Valley - 2019.07.01 - (DAI) OG Cream Cheese.xlsx"
del "PLOG - Organic Valley - 2019.07.07 - (DAI) OG Cream Cheese.xlsx"

答案 1 :(得分:0)

符合作者实际文件名的新版本

@(
  SetLocal EnableDelayedExpansion
  ECHO OFF
  SET "_PathToCheck=Y:\T\DT"
  SET "_FileGlob=PLOG - * - ????.??.?? - *.xlsx"
  SET "_CurrentFile="
)

FOR /F "Tokens=1-2* Delims=-" %%A IN ('
  DIR /A-D /O-N /B "%_PathToCheck%\%_FileGlob%"
') DO (
  IF /I "!_CurrentFile!" EQU "%%A-%%B" (
    ECHO.Deleting: "%_PathToCheck%\%%A-%%B-%%C"
    DEL /F /Q "%_PathToCheck%\%%A-%%B-%%C"
  ) ELSE (
    ECHO.
    ECHO.New File Found: "%%A-%%B"
    ECHO.-----------
    ECHO.Retaining: "%_PathToCheck%\%%A-%%B-%%C"
    SET "_CurrentFile=%%A-%%B"
  )
)

示例输出:

Y:\>Y:\t\DT.cmd

New File Found: "PLOG - File Three For yoU "
-----------
Retaining: "Y:\T\DT\PLOG - File Three For yoU - 2019.08.11 - (something) AAA 1 .xlsx"

New File Found: "PLOG - File Number Two "
-----------
Retaining: "Y:\T\DT\PLOG - File Number Two - 2019.12.19 - Ending ABDC 1111 AB.xlsx"
Deleting: "Y:\T\DT\PLOG - File Number Two - 2019.07.30 - Ending ABDC 1111 AB.xlsx"
Deleting: "Y:\T\DT\PLOG - File Number Two - 2019.03.12 - Ending Number 3 .xlsx"

New File Found: "PLOG - File Number One "
-----------
Retaining: "Y:\T\DT\PLOG - File Number One - 2020.01.01 - Ending BBB .xlsx"
Deleting: "Y:\T\DT\PLOG - File Number One - 2019.12.19 - Ending BBB 2 .xlsx"
Deleting: "Y:\T\DT\PLOG - File Number One - 2019.09.07 - Ending AAA1.xlsx"
Deleting: "Y:\T\DT\PLOG - File Number One - 2017.01.03 - Ending AAA 1 .xlsx"

Y:\>

屏幕截图,该脚本可以正常运行并显示输出和结果:

Confirms the script works as described.

基本上,这与我的原始版本具有相同的作用,只是现在我们知道我们应该寻找连字符

IE:

我们使用DIR以相反的顺序对文件名进行排序,这意味着具有较新日期的文件将出现在具有较旧日期的文件之前。

这简化了删除文件的逻辑,也是我原始解决方案的症结所在。

由于使用该方法,我们只需要检查文件名的第一部分(日期之前的部分)是否与找到的先前文件相同。

我们通过创建一个变量来保存当前文件_CurrentFile的名称并将其设置为空,因此在进行初始检查时,它将不匹配任何文件名。

如果_CurrentFile与找到的文件目录的文件名的第一部分(再次是日期之前的部分)匹配,那么我们可以安全地删除它。

如果_CurrentFile与DIR cmd报告的文件的有趣部分不匹配,则我们将_CurrentFile变量更新为该新值,然后移至下一个要测试的文件结果。 / p>

由于您不熟悉cmd / batch脚本,因此我想花一点时间详细了解该脚本的功能以及执行该操作的原因,以便您可以继续前进:

首先,我要注意的是,我们有一些关于如何循环访问文件的选项,最常见的是for, for/FFor files遍历文件,有时在DIR中使用for /F cmd,或者在文件列表中使用WMIC(尽管值得庆幸的是,WMIC最终不赞成使用Powershell而已。)

我们知道您只是想根据文件名和存储在文件名中的日期选择,然后使用dir cmd按名称排序将是一种实用的方法,可以快速进行匹配

现在了解脚本的每个部分在做什么

@(

通过括号在CMD和批处理脚本中创建代码块,将同时评估给定括号中的所有内容。

通过将@放在括号前面,任何带有命令的命令(并且不在括号内或DO之后)都不会在屏幕上回显。这是为了阻止此部分表单显示出来并使输出混乱。

  SetLocal EnableDelayedExpansion

我们正在启用延迟扩展,以使我们可以通过使用for而不是!_var!引用变量来轻松评估%_Var%循环内变量的内容,从技术上讲,我们可以如果您的文件名中包含!,请不要使用它,我们应该禁用它并重新写入一点,如果没有,那就没问题了。

  ECHO OFF

我正在阻止脚本回显正在执行的每一行,因此我们的混乱输出减少了。设置此命令意味着我不再需要在此代码块内的其他命令之前或此块外的将来代码之前使用@。

  SET "_PathToCheck=Y:\T\DT"
  SET "_FileGlob=PLOG - * - ????.??.?? - *.xlsx"
  SET "_CurrentFile="
)

设置变量并使用右圆括号关闭代码块似乎是不言自明的 _FileGlob

这是一个标准的文件Glob,用于匹配您要考虑进行比较的文件名。

*多次匹配任何字符,?一次匹配任何字符。   这样可以确保,如果遇到的文件与我们期望的格式不符,我们可以跳过它们。

如果需要进行更明确的匹配,我们可以使用*.xlsx来代替,并使用FINDStr来检查正则表达式模式,以确保格式完全符合需要。

在下一部分

 FOR /F "Tokens=1-2* Delims=-" %%A IN ('
  DIR /A-D /O-N /B "%_PathToCheck%\%_FileGlob%"
') DO (
   [Code]
)

现在我要在这里有点混乱:

我们正在使用DIR快速按照相反的名称对文件进行排序,并仅返回文件名。 DIR的执行速度非常快,因此,如果您要进行一点排序而不是稍后使用IF比较来匹配文件,则它是更可取的。如上所述,我们利用文件全局来确保仅返回要评估的文件。

选项/A-D忽略目录,/B仅输出文件名(因为我们不递归)。然后,我们得到/O-N-/O是“选项N的“排序依据”按名称升序排列,而-N按反向(降序)顺序(IE ZA 9-0)按名称排序,因此可以确保文件名称为最新日期将是我们找到的第一个日期。

所有内容都放在For /F循环中,这是解析命令输出的一种方式。我们使用Delims=-来“标记化”或拆分FOR从DIR命令接收的字符串。我们使用FOR告诉%%A使用什么变量名称来存储令牌(变量如下:“ ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ]”或“ _ `` a b c d e f g h i j k l m n o p q r s t u v w x y z” {{这里的更多信息{{3 }})),从您选择的变量开始,将变量分配给令牌。

当我们指定要选择的令牌时,Tokens=1-2*",特别是1-2意味着将第一个令牌通过第二个令牌,并将它们存储在前N个变量中(其中N =变量数在集合1-2中,即%%A%%B(为了我们的目的),而*意味着在此之前提及的任何标记之后停止标记任何内容,并将所有剩余部分放置的行到下一个变量(%%C)。

由于我们要使用连字符作为定界符进行标记化,因此我们现在前两个标记将是PLOG[Name to Compare,而日期和其余文件名将在第三个标记中

DO ( )部分中,我们将继续处理每行返回并存储在令牌中的信息。

让我们继续检查DO ( )内的代码

  IF /I "!_CurrentFile!" EQU "%%A-%%B" (
    ECHO.Deleting: "%_PathToCheck%\%%A-%%B-%%C"
    DEL /F /Q "%_PathToCheck%\%%A-%%B-%%C"
  ) ELSE (
    ECHO.
    ECHO.New File Found: "%%A-%%B"
    ECHO.-----------
    ECHO.Retaining: "%_PathToCheck%\%%A-%%B-%%C"
    SET "_CurrentFile=%%A-%%B"
  )

您可能已经很熟悉VBA,但是您正在测试字符串前两个部分的变量_CurrentFile的值,我们知道这是文件的整个部分命名为日期,我们需要重新添加连字符,因为当FOR按令牌拆分时,它将删除这些令牌。

我们检查_CurrentFile变量是否与当前返回的文件名部分匹配,但不包括日期。

如果匹配,我们将删除(Del文件,因为我们之前已经看过该文件一次,因此该文件较旧。

我们使用/F选项强制删除只读文件,然后使用/Q阻止它提示我们确认每个文件的删除。

我们也ECHO.正在删除发现的文件,以记录脚本的作用。

  ) ELSE (

如果不匹配,则表示这是我们尚未遇到的新文件,并且必须是返回的第一个文件,在这种情况下,我们希望保留该文件,因为我们从Dir的种类中知道它将成为有趣的文件。

因此,在不匹配的情况下,我们将变量_CurrentFile更改为保留前两个标记%%A-%%B的值,以供将来检查返回的结果时使用。

我们也ECHO.找到了文件,并将其保留下来以很好地表明脚本的作用。

关于ECHO的进一步说明-尽管我喜欢Echo.的外观,但是ECHO(使用起来更安全,因此我更喜欢它,但对人们来说却更令人困惑那些不熟悉cmd脚本的人,因为“开括号”看起来像我有错字或未封闭的代码块,并可能导致人们认为它会引起一些问题。因此,出于这种原因,我尝试避免在ECHO(可行的情况下使用ECHO.来支持ECHO.

使用错误格式的原始文章和版本

您可以使它成为一个非常简单的脚本,基本上可以找到每个唯一的名称并保留第一个,只要您的名称为YYYY.MM.DD.xlsx格式即可,方法是对名称进行预排序,以便名称中带有最新日期的始终是遇到的第一个文件。

空间有保证吗?可选的?

为此,您需要使用FOR /F循环来分析{DIR)名称降序(/O)排序的-N的输出

DT.CMD:

@(
  SetLocal EnableDelayedExpansion
  ECHO OFF
  SET "_PathToCheck=Y:\T\DT"
  SET "_FileGlob=* ????.??.??.xlsx"
  SET "_CurrentFile="
)

FOR /F "Tokens=*" %%A IN ('DIR /A-D /O-N /B "%_PathToCheck%\%_FileGlob%"') DO (
  SET "_TFile=%%~nA"
  SET "_TFile=!_TFile:~0,-10!"
  IF /I "!_CurrentFile!" EQU "!_TFile!" (
    ECHO.Deleting: "%_PathToCheck%\%%~A"
    DEL /F /Q "%_PathToCheck%\%%~A"
  ) ELSE (
    ECHO.
    ECHO.New File Found: !_TFile!
    ECHO.-----------
    ECHO.Retaining: "%_PathToCheck%\%%~A"
    SET "_CurrentFile=!_TFile!"
  )
)

然后,我们只需要比较尾随YYYY.MM.DD.xlsx以外的文件名,如果文件名是第一个,则保留该文件名,因为我们知道它将是最新的。 / p>

如果名称重复,我们可以删除它,因为我们知道我们已经跳过了最新的名称。

示例输出:

Y:\>Y:\t\DT.cmd

New File Found: bananas 
-----------
Retaining: "Y:\T\DT\bananas 2019.07.01.xlsx"

New File Found: oranges 
-----------
Retaining: "Y:\T\DT\oranges 2019.09.01.xlsx"
Deleting: "Y:\T\DT\oranges 2019.07.11.xlsx"

New File Found: apples 
-----------
Retaining: "Y:\T\DT\apples 2019.07.07.xlsx"
Deleting: "Y:\T\DT\apples 2019.07.01.xlsx"

如果您的日期格式改为YYYY.DD.MM.Xlsx

然后,您将需要经历一两个额外的箍。

基本上在这种情况下,我们可以执行以下操作:

将文件名另存为具有文件名的更正(可排序)版本(YYYY.MM.DD格式)的变量,然后对其进行排序,然后比较变量数组,删除不是最新的变量。 / p>

以下是版本DT_DM.CMD:

@(
  SetLocal EnableDelayedExpansion
  ECHO OFF
  SET "_PathToCheck=Y:\T\DT"
  SET "_FileGlob=* ????.??.??.xlsx"
  SET "_CurrentFile="
  SET "_MatchList= "
)

FOR /F "Tokens=*" %%A IN ('DIR /A-D /ON /B "%_PathToCheck%\%_FileGlob%"') DO (
  SET "_TFile=%%~nA"
  SET "_TFileMD=!_TFile:~-5!"
  SET "_TVar=__!_TFile:~0,-5!!_TFileMD:~-2!.!_TFileMD:~0,2!"
  REM ECHO.Storing File: "%%~A" As: "!_TVar!"
  SET "!_TVar!=%%~A"
  IF /I "!_CurrentFile!" NEQ "!_TFile:~0,-10!" (
    ECHO.New File Found, Adding to Sort List: "!_TFile:~0,-10!"
    SET "_CurrentFile=!_TFile:~0,-10!"
    SET "_MatchList=!_MatchList! "__!_TFile:~0,-10!""
  )
)

ECHO.
ECHO.Delete Old Files
ECHO.-----------------

REM Loop the Matched Files:
FOR %%a IN (%_MatchList%) DO (
ECHO.
ECHO.Delete Old %%a Files
ECHO.-----------------
  REM Loop the SET sorted for each File Found and Skip the First one (Newest), deleting the others.
  FOR /F "Skip=1 Tokens=1-2 Delims==" %%A IN ('SET "%%~a" ^| SORT /R') DO (
    ECHO.Deleting: "%_PathToCheck%\%%~B"
    DEL /F /Q "%_PathToCheck%\%%~B"
    REM Remove the deleted file variable so we can print a list of retained files at the end:
    SET "%%A="
  )
)

ECHO.
ECHO.Retained Files:
ECHO.-----------------
FOR %%a IN (%_MatchList%) DO ( SET "%%~a" )

以下是示例输出:

Y:\>Y:\t\DT_DM.cmd
New File Found, Adding to Sort List: "apples "
New File Found, Adding to Sort List: "bananas "
New File Found, Adding to Sort List: "oranges "

Delete Old Files
-----------------

Delete Old "__apples " Files
-----------------
Deleting: "Y:\T\DT\apples 2019.07.07.xlsx"
Deleting: "Y:\T\DT\apples 2019.12.01.xlsx"

Delete Old "__bananas " Files
-----------------

Delete Old "__oranges " Files
-----------------

Retained Files:
-----------------
__apples 2019.12.01=apples 2019.01.12.xlsx
__bananas 2019.01.07=bananas 2019.07.01.xlsx
__oranges 2019.11.07=oranges 2019.07.11.xlsx

现在,这两个示例都假定您始终希望使用最新日期命名的文件,而不是最近修改的文件

可能是这种情况,因为我知道在处理自己的过时文件时,通常会遇到这种情况,以防有人或某些进程来修改文件,或者我保存了多个乱序的文件。 / p>

但是,如果您真的只想保留最近修改的文件,我们可以使用与第二个版本相同的概念,并将“实际修改时间”保存为变量,而不是日期。

DT_Modified.CMD:

@(
  SetLocal EnableDelayedExpansion
  ECHO OFF
  SET "_PathToCheck=Y:\T\DT"
  SET "_FileGlob=*.xlsx"
  SET "_CurrentFile="
  SET "_MatchList= "
)

FOR %%A IN ("%_PathToCheck%\%_FileGlob%") DO (
  ECHO.%%A| FINDStr /I " [0-9][0-9][0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.xlsx$" >NUL && (
    SET "_TFile=%%~nA"
    SET "_TVar=__!_TFile:~0,-10!%%~tA"
    ECHO.Storing File: "%%~A" As: "!_TVar!"
    SET "!_TVar!=%%~A"
    IF /I "!_CurrentFile!" NEQ "!_TFile:~0,-10!" (
      ECHO.
      ECHO.New File Found, Adding to Sort List: "!_TFile:~0,-10!"
      ECHO.
      SET "_CurrentFile=!_TFile:~0,-10!"
      SET "_MatchList=!_MatchList! "__!_TFile:~0,-10!""
    )
  )
)

ECHO.
ECHO.Delete Old Files
ECHO.-----------------

REM Loop the Matched Files:
FOR %%a IN (%_MatchList%) DO (
ECHO.
ECHO.Delete Old %%a Files
ECHO.-----------------
  REM Loop the SET sorted for each File Found and Skip the First one (Newest), deleting the others.
  FOR /F "Skip=1 Tokens=1-2 Delims==" %%A IN ('SET "%%~a" ^| SORT /R') DO (
    ECHO.Deleting: "%_PathToCheck%\%%~B"
    DEL /F /Q "%_PathToCheck%\%%~B"
    REM Remove the deleted file variable so we can print a list of retained files at the end:
    SET "%%A="
  )
)

ECHO.
ECHO.Retained Files:
ECHO.-----------------
FOR %%a IN (%_MatchList%) DO ( SET "%%~a" )

第一个运行脚本的广告结果示例:

https://ss64.com/nt/for_f.html