Как мога да сортирам изображения въз основа както на дата, така и на родителска папка?

Имам задача да създавам ежемесечно архивиране на снимки, те трябва да бъдат преместени от мястото, където са на друго място, сортирани по дата в папки, след което записвани на диск на месечни партиди.
На теория това трябва да е просто, дори въпреки че това е нов процес и имаме снимки за приблизително една година, след като бъде готово, можем просто да го стартираме всеки месец отсега нататък.

Така че първата мисъл е:
Копирайте от място "A" до местоположение "B", сортирайте в подпапки въз основа на датата на създаване "MONTH\YEAR", запишете всяка папка на диск.

Това е доста просто и щеше да бъде направено сега, ако случаят беше такъв, проблемът ми е следният:
Всички снимки са от други компании, за които предоставяме услуги, има ~15 компании и всяка има своя собствена папка. Във всяка папка има още 5 папки, ЕДНА от които има изображения, други имат резервни копия на база данни с конфигурационни файлове и т.н.

Така че като пример структурата е:

─Данни
├─Компания1
│ ├───ИЗОБРАЖЕНИЯ
│ ├───Друго1
│ └───Друго2
├─Компания2
│ ├───ИЗОБРАЖЕНИЯ
│ ├───Друго1
│ └───Друго2
└─Компания3
├───ИЗОБРАЖЕНИЯ
├───Друго1
└───Друго2

Така във всяка папка „ИЗОБРАЖЕНИЕ“ са снимките, които не са сортирани по дата. Трябва да копирам само тези папки в структура, която изглежда така:

──Резервни копия
├─01-15
│ ├───Компания1
│ ├───Компания2
│ └───Компания3
├─02-15
│ ├───Компания1
│ ├───Компания2
│ └───Компания3
└─03-15
├───Компания1
├───Компания2
└───Компания3

По този начин мога просто да запиша всяка месечна папка на собствен диск и в нея всички изображения да са сортирани по компания.

Така че създайте папка с име "MONTH-YEAR", след това вътре в нея създайте папки за всяка компания "Company1", "Company2" и т.н. и след това във всяка от тези папки има снимки от тази компания и този месец.

Опитах се да напиша това на пакет до почти 0 успеха. Въпреки че вече нямам кода (изтрих го от раздразнение, след като не работи няколко твърде много пъти :/), той използва много вложени IF изрази и променливи, които са зададени да поддържат името на родителската папка на родителската папка и т.н. и т.н., след което се завърта, за да извърши целия процес в следващата папка...

Накратко, имам нужда от някакъв начин за скриптиране на преместването на изображения в папки въз основа на датата и името на папката две нива над нея. Също така, ако може да създаде папките, в които се копира, това би било страхотно.
Дори ако някой може просто да предостави някои указания откъде да започна в batch (или VBS), или някакъв псевдо код, върху който мога да работя бих го оценил МНОГО.

Благодаря ви много,
този объркан новак!


person Keeghan McGarry    schedule 09.01.2015    source източник
comment
Откъде идва датата? Датата на последна промяна? Създадена дата? друго?   -  person dbenham    schedule 09.01.2015


Отговори (2)


Така че, вместо да изтриете името на компанията от папката на изображението, приемливо ли е да го изтриете от дъщерния елемент на „Данни“? Това може да се направи с известна манипулация на низ в подпрограма.

В for цикъл, %%~tX има последната модифицирана дата на всеки файл. Използването на няколко вложени for цикъла за разделяне на този низ чрез интервал, след това чрез наклонени черти може много лесно да масажира датата в MM-YYYY. Редактиране: потърсих формата на датата за локала на Нова Зеландия, и мисля, че съм конфигурирал правилно скрипта да изтрива месец и година вместо вас. Опитайте го и вижте как работи. (Ако се опитвах да направя този локален агностик, вероятно щях да направя заявка 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%"
person rojo    schedule 09.01.2015
comment
Благодаря ти! Това работи перфектно, единствената модификация, която направих, беше да го променя от създаване на папка, наречена MM-YYYY, на YYYY-MM, както е предложено в другия отговор. - person Keeghan McGarry; 11.01.2015

Тези решения предполагат, че списъците с директории показват клеймото за време на файла като „MM/DD/YYYY HH:MM:SS PM“. Кодът трябва да бъде променен, ако форматът на времевия печат е различен за вашия локал.

Горещо ви препоръчвам да наименувате вашите папки с дати, като използвате формат ГГГГ-ММ, така че да се сортират хронологично. Освен това не трябва да използвате 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
  )
)
person dbenham    schedule 09.01.2015
comment
РЕДАКТИРАНЕ - Добавени методи, които ще работят с всеки формат на дата, независимо от локала. Първият използва WMIC и е сложен. Вторият използва моята помощна програма JREN.BAT - много по-проста :) - person dbenham; 09.01.2015
comment
Въпреки че оценявам помощта и въведението в JREN.BAT (което ще използвам в бъдеще и виждам, че е полезно), другият отговор избрах заради неговата простота. - person Keeghan McGarry; 11.01.2015
comment
@KeeghanMcGarry - отговорът на rojo е напълно добър, но моето първо решение за чиста партида изглежда толкова просто за мен. Различни удари предполагам :) - person dbenham; 12.01.2015