如何根据日期和父文件夹对图像进行排序?

How can I sort images based on both date and parent folder?

我的任务是创建照片的每月备份,这些照片需要从它们所在的地方移动到其他地方,按日期分类到文件夹中,然后按月批量刻录到磁盘。
理论上这应该很简单,即使它是一个新过程并且我们有大约一年的照片价值,一旦完成一次我们就可以从现在开始每个月运行它。

所以首先想到的是:
从位置 "A" 复制到位置 "B",根据创建日期 "MONTH\YEAR" 分类到子文件夹中,将每个文件夹刻录到磁盘。

这很简单,如果是这样的话,现在就可以完成了,我的问题是:
所有的照片都来自我们提供服务的其他公司,大约有 15 家公司,每家公司都有自己的文件夹。每个文件夹内还有 5 个文件夹,其中一个有图像,其他有配置文件数据库备份等。

举个例子,结构是:

─Data
├─Company1
│ ├───IMAGES
│ ├───Other1
│ └───Other2
├─Company2
│ ├───IMAGES
│ ├───Other1
│ └───Other2
└─Company3
├───IMAGES
├───Other1
└───Other2

因此,每个 "IMAGE" 文件夹中的照片 按日期排序。我只需要将那些文件夹复制到如下所示的结构中:

──Backups
├─01-15
│ ├───Company1
│ ├───Company2
│ └───Company3
├─02-15
│ ├───Company1
│ ├───Company2
│ └───Company3
└─03-15
├───Company1
├───Company2
└───Company3

这样我就可以简单地将每个月的文件夹刻录到它自己的磁盘上,并且其中所有图像都按公司排序。

所以创建一个名为 "MONTH-YEAR" 的文件夹,然后在其中为每个公司创建文件夹 "Company1" "Company2" 等,然后在每个文件夹中都有该公司的照片和那个月。

我试过批量写这个,几乎成功了0次。虽然我不再有代码(在它没有工作太多次后烦恼地删除了它:/),它使用了很多嵌套的 IF 语句和变量被设置来维护父文件夹等的父文件夹的名称等然后循环在下一个文件夹上完成整个过程...

长话短说,我需要一些方法来根据日期和上两级文件夹的名称编写将图像移动到文件夹的脚本。另外,如果它可以创建将其复制到的文件夹,那就太好了。
即使有人可以提供一些关于从哪里开始批量(或 VBS)的指示,或者一些我可以使用的伪代码,我也会非常感激。

非常感谢,
这个糊涂的新手!

那么,不是通过图像的祖父文件夹来抓取公司名称,而是通过 "Data" 的子文件夹来抓取公司名称是否可以接受?这可以通过子程序中的一些 string manipulation 来完成。

for 循环中,%%~tX 具有每个文件的最后修改日期。使用几个嵌套的 for 循环将该字符串拆分为 space,然后通过斜杠可以非常轻松地将日期修改为 MM-YYYY编辑:looked up the date format 新西兰语言环境,我想我已经正确配置了脚本,可以为您抓取月份和年份。试一试,看看它是如何工作的。 (如果我试图让这个语言环境不可知,我可能会为每个文件查询 wmic datafile,但这有点麻烦。如果你需要它,请告诉我。)

@echo off
setlocal enabledelayedexpansion

set "source=c:\Data"
set "destination=d:\Backup"

:: for every image file recursively in %source%
for /r "%source%" %%I in (*.jpg *.png *.gif *.bmp *.xcf *.psd) do (

    rem :: get company name from directory 1 level within "Data"
    call :getCompany company "%%~dpI"

    rem :: get month-year from file last modified date
    rem :: note: assumes ~t is in format of dd/mm/yy hh:mm
    for /f %%a in ('echo %%~tI') do (
        for /f "tokens=2,3 delims=/" %%x in ('echo %%a') do (
            set "fdate=%%x-%%y"
        )
    )

    set "folder=%destination%\!fdate!\!company!\"
    if not exist "!folder!" md "!folder!"

    <NUL set /P "=Copying %%~nxI to !fdate!\!company!\... "
    >NUL copy "%%~fI" "!folder!"
    echo Done.
)

goto :EOF

:getCompany <var_to_set> <path>
setlocal
set "company=%~2"
set "company=%company:*Data\=%"
set company=%company:\=&rem.%
endlocal & set "%~1=%company%"

这些解决方案假定目录列表将文件时间戳显示为 "MM/DD/YYYY HH:MM:SS PM"。如果您的区域设置的时间戳格式不同,则必须修改代码。

我强烈建议您使用 YYYY-MM 格式命名日期文件夹,以便它们按时间顺序排序。另外,您不应该使用 2 位数年份 - 我经历过 Y2K 噩梦 - 这不值得。

如果日期来自每个文件的最后修改日期,那么这很容易 - ~t 修饰符提供文件的最后修改日期:

%= Iterate company folders =%
for /d %%D in ("Data\*") do (

  %= Iterate images under company folder =%
  for %%F in ("%%D\IMAGES\*") do (

    %= Parse last modified month and year for the file =%
    for /f "delims=/ tokens=1,3" %%A in ("%%~tF") do (

      %= Create the destination folder - ignore error if already exists =%
      md "Backup\%%B-%%A\%%~nxD" 2>nul

      %= Move the file =%
      move "%%F" "Backup\%%B-%%A\%%~nxD" >nul
    )
  )
)

如果你想要创建日期,那就有点复杂了。我使用 DIR /TC 选项来获取创建日期,并且必须从 DIR 列表中解析日期和文件名。

%= Iterate company folders =%
for /d %%A in ("Data\*") do (

  %= Iterate images under company folder, parsing out created date and file name =%
  for /f "tokens=1,4*" %%B in (
    'dir /a-d /tc "%%A\images"^|findstr /rc:"^[^ ]"'
  ) do (

    %= Parse the month and year from created date =%
    for /f "delims=/ tokens=1,3" %%E in ("%%B") do (

      %= Create the destination folder - ignore error if already exists =%
      md "Backup\%%C-%%B\%%~nxA" 2>nul

      %= Move the file =%
      move "%%A\images\%%D" "Backup\%%C-%%B\%%~nxA" >nul
    )
  )
)

可以使用 WMIC 以与区域设置无关的方式获取创建日期或上次修改日期。但是编码变得相当复杂,需要许多神秘的技巧才能有效地获得所需的结果。下面的代码假定您想要创建日期。要使用上次修改日期,只需将 creationDate 更改为 lastModified

@echo off
:: FOR loop variables must be expanded with delayed expansion off
:: otherwise values containing ! will be corrupted
setlocal disableDelayedExpansion

%= Iterate company folders =%
for /d %%D in ("Data\*") do (

  %= Parse drive, company, and image path info                    =%
  %= Enable delayed expansion to access variables set within loop =%
  %= Escape \ in path for use with WMIC                           =%
  set "drive=%%~dD"
  set "company=%%~nxD"
  set "imagePath=%%~pnxD"
  setlocal enableDelayedExpansion
  set "imagePath=!imagePath:\=\!\IMAGES\"

  %= Iterate images and creation dates for files under company folder using WMIC   =%
  %= Two FOR /F loops are used to eliminate trailing <CR> character from each line =%
  %= The trailing <CR> is an odd artifact of conversion of WMIC unicode output     =%
  for /f "skip=1 delims=" %%A in (
    'wmic datafile where "drive='!drive!' and path='!imagePath!'" get creationDate^, name 2^>nul'
  ) do for /f "tokens=1*" %%B in ("%%A") do (

    %= Disable delayed expansion again to protect ! =%
    setlocal disableDelayedExpansion

    %= Save the date and file info. The file will have trailing spaces, but that =%
    %= is OK because Windows ignores trailing spaces and dots in file names      =%
    set "date=%%B"
    set "image=%%C" 

    %= Re-enable delayed expansion and parse out year and month to compute destination =%
    setlocal enableDelayedExpansion
    set "destination=Backup\!date:~0,4!-!date:~4,2!\!company!"

    %= Create the destination folder - ignore error if already exists =%
    md "!destination!" 2>nul

    %= Move the file =%
    move "!image!" "!destination!" >nul

    %= Pop the setlocal stack to get back to state before inner loop started =%
    endlocal
    endlocal
  )

  %= Pop the setlocal stack to get back to state before outer loop started =%
  endlocal
)

如果您使用我的 JREN.BAT 实用程序,这项工作会变得容易得多,它可以以与语言环境无关的方式解析和格式化文件时间戳。 JREN.BAT 主要用于使用正则表达式重命名文件。但它有一个列表模式,允许您将文件名转换为几乎任何内容,而无需重命名,然后可以使用 FOR /F 进行解析。

下面的代码使用创建日期。如果您想改用上次修改日期,请将 created 更改为 modified

@echo off

:: JREN does most of the work.
:: /LIST option lists the final "rename" value, without renaming anything
:: /P specifies root path
:: /S iterates all sub-folders
:: /PM determines which sub-folders to look at, /P: matches the root path
:: /J treats replacement value as JScript code
:: The "^.*" search matches the entire file name
:: The "ts({dt:'created',fmt:'{yyyy}-{mm} '})+path()" replacement 
::    gets and formats the creation date using WMI
::    followed by the full path to the image file
:: The JREN output looks like:  YYYY-MM fullPathToImage

for /f "tokens=1*" %%A in (
  'jren "^.*" "ts({dt:'created',fmt:'{yyyy}-{mm} '})+path()" /list /j /s /p "Data" /pm "/P:\*\IMAGES"'
) do (

  %= Get the company name from the fullImagePath =%
  for %%C in ("%%B\..\..") do (

    %= Create the destination folder - ignore error if already exists =%
    md "Backup\%%A\%%~nxC" 2>nul

    %= Move the file =%
    move "%%B" "Backup\%%A\%%~nxC" >nul
  )
)