У меня есть много файлов в этом формате:
D:\images\AAA_BBB\image\whatever1.jpg
D:\images\AAA_BBB\image\whatever2.jpg
D:\images\FFF_EEE_CCC\image\asdf1.jpg
D:\images\FFF_EEE_CCC\image\asdf2.jpg
D:\images\WWW_XXX_YYY_ZZZ\image\someimage1.jpg
D:\images\WWW_XXX_YYY_ZZZ\image\someimage2.jpg
Я хотел бы переместить и переименовать эти файлы, используя подстроку абсолютного пути к файлу, и удалить исходные файлы, чтобы результаты выглядели примерно так, как показано ниже, начиная с 0
.
D:\images\AAA\AAA_0.jpg
D:\images\AAA\AAA_1.jpg
D:\images\EEE\EEE_0.jpg
D:\images\EEE\EEE_1.jpg
D:\images\YYY\YYY_0.jpg
D:\images\YYY\YYY_1.jpg
Обратите внимание, что количество токенов в папке со знаками подчеркивания может быть любой длины.
Следующая попытка даст мне только второй жетон, начиная с фронта. Использование «Tokens=-2» для получения второго последнего токена ничего не выведет.
@echo off & setlocal EnableDelayedExpansion
PushD "D:\images" || Exit /B
For /F "EOL=? Delims = " %%G In ('Dir /A:D /B 2^>NUL
^| %SystemRoot%\System32\findstr.exe /I /R "^[^_][^_]*_[^_][^_]*_[^_][^_]*_"'
) Do For /F "Tokens=2 EOL=? Delims=_" %%H In ("%%G") Do (
Set "i=-1"
For /F "EOL=? Delims = " %%I In ('Dir "%%G\image" /A:-D /B') Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
Ren "%%G\image\%%I" "%%H_!i!%%~xI" && (
If Not Exist "%%H\." MD "%%H"
Move /Y "%%G\image\%%H_!i!%%~xI" "%%H"
)
EndLocal
)
)
PopD
Затем я использовал другую логику, попробовав два цикла for
, чтобы получить последний токен, а затем второй последний токен, но это не сработало.
@echo off & setlocal EnableDelayedExpansion
PushD "D:\images" || Exit /B
For /F "EOL=? Delims = " %%G In ('Dir /A:D /B 2^>NUL
^| %SystemRoot%\System32\findstr.exe /I /R "^[^_][^_]*_[^_][^_]*_[^_][^_]*_"'
) Do (
Set "dirName=%%G"
Set "lastSegment = "
For %%H In ("!dirName:_ = " "!") Do (
Set "secondLastSegment=!lastSegment!"
Set "lastSegment=%%~H"
)
Set "segment=%%~xG"
Set "i=-1"
For /F "EOL=? Delims = " %%I In ('Dir "%%G\image" /A:-D /B') Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
Ren "%%G\image\%%I" "!secondLastSegment!_!i!!segment!" && (
If Not Exist "!secondLastSegment!\." MD "!secondLastSegment!"
Move /Y "%%G\image\!secondLastSegment!_!i!!segment!" "!secondLastSegment!"
)
EndLocal
)
)
PopD
Основываясь на предоставленной вами информации, поможет ли это вам?
@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "BaseDir=D:\images"
PushD "%BaseDir%" 2>NUL || Exit /B
For /F "EOL=? Delims = " %%G In ('Dir "*_*" /A:D-L /B /O:N 2^>NUL
^| %SystemRoot%\System32\findstr.exe /IR "[^_][^_]*_[^_][^_]*"') Do (
PushD "%%G"
For /F "Tokens=1,* Delims=:" %%H In ('Dir "image\*.jpg" /A:-D-L /B 2^>NUL
^| %SystemRoot%\System32\findstr.exe /INR "\.jpg$"') Do (
For /F "Delims=_" %%J In ("%%G") Do (
If Not Exist "%BaseDir%\%%J\." MD "%BaseDir%\%%J"
Move /Y "%BaseDir%\%%G\image\%%~I" "%BaseDir%\%%J\%%J_%%H%%~xI"
) 1>NUL
)
PopD
)
PopD
Имейте в виду, что у вас могут быть пустые каталоги, которые вы также можете очистить!
@ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=u:\images"
FOR /f %%e IN ('DIR /b /ad "%sourcedir%\*_*"') DO (
rem %%e has firstlevel subdirectory name - must have at least one "_"
SET "L2=%%e"
FOR %%o IN (!L2:_^= !) DO SET "destsub=!prevpart!"&SET "prevpart=%%o"
FOR /f %%y IN ('dir /b /a-d "%sourcedir%\%%e\image\*.jpg"') DO (
MD "%sourcedir%\!destsub!" >nul 2>NUL
SET "needtomove=Y"
FOR /L %%u IN (0,1 9999) DO IF DEFINED needtomove IF NOT EXIST "%sourcedir%\!destsub!\!destsub!_%%u.jpg" move "%sourcedir%\%%e\image\%%y" "%sourcedir%\!destsub!\!destsub!_%%u.jpg" >nul&SET "Needtomove = "
)
)
GOTO :EOF
Всегда сверяйтесь с тестовым каталогом перед применением к реальным данным.
Найдите все подкаталоги, содержащие _
. Найдите предпоследний элемент, разделенный _
, заменив _
s пробелами и используя for
. Для каждого *.jpg
в ssubdirectory\image попытайтесь указать его место назначения (2> nul подавляет сообщения об ошибках, такие как «Каталог уже существует»), затем попытайтесь найти первое доступное имя в целевом каталоге (поскольку нет гарантии, что случайные файлы не исчезнут). t уже существуют в пункте назначения) и сделайте ход.
Это должно работать:
@echo off
setlocal EnableDelayedExpansion
pushd "D:\images" || exit /B
for /D %%d in (*_*) do (
set "oldName=%%d"
for %%b in (".!oldName:_=.!") do for %%c in ("%%~Nb") do set "newName=%%~Xc"
set "newName=!newName:~1!"
md "!newName!"
set "i=-1"
for %%f in ("%%d\image\*.jpg") do (
set /A i+=1
move "%%~f" "!newName!\!newName!_!i!%%~Xf"
)
)
В ваших примерах много ненужного или избыточного кода...
for /F
, когда вы просто хотите обработать файлы или папки; обычные for
или for /D
проще и работают быстрее, чем for /F
.for /D %%d in (*_*) do
обрабатывает все папки хотя бы с одним символом подчеркивания; Я не понимаю, почему вы используете findstr
со сложным регулярным выражением...~Name
и ~eXtension
FOR.В вашем коде есть несколько проблем, которые я хочу устранить:
findstr
слишком строг, поэтому он позволит обрабатывать только два последних изображения в вашем списке примеров. Позвольте мне порекомендовать полностью удалить его и вместо этого использовать маску каталога *_*
.if exist
приводит к неправильному результату при добавлении \.
к пути (когда он указывает на файл, а не на каталог, он также сообщается как существующий); вместо этого следует добавить \*
, чтобы файлы считались несуществующими.*.jpg
и/или *.png
, для файлов изображений, которые будут обрабатываться, чтобы отфильтровать непреднамеренные.segment
будет пустой, потому что %%~xG
будет пустой (исходя из ваших примеров). Вы, скорее всего, имели в виду %%~xI
.setlocal
для локализации среды использование pushd
в большинстве случаев не требуется, поскольку setlocal
также включает в себя текущий рабочий каталог. Помните, что отсутствие popd
может оставить нежелательные записи стека pushd
, в отличие от setlocal
, потому что endlocal
всегда неявно выполняется, когда пакетный скрипт завершается.!
или ^
в своих именах, могут вызвать проблемы.Вот ваш код с указанными исправленными проблемами (не касаясь внутренней методологии):
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Usage of `pushd`/`popd` is usually not needed when using `setlocal/`endlocal`:
cd /D "%~dp0images" || exit /B
for /F "eol=| delims = " %%G in ('
rem/ // The `findstr` filter was too strong and is unnecessary ^(a simple `dir` is enough^)^^! ^& ^
2^> nul dir /B /A:D-H-S-L "*_*"
') do (
set "dirName=%%G"
set "lastSegment = "
rem // Toggle delayed expansion to have it enabled only when necessary to not lose `!`-symbols:
setlocal EnableDelayedExpansion
for %%H in ("!dirName:_ = " "!") do for %%J in ("!lastSegment!") do (
endlocal
set "secondLastSegment=%%~J"
set "lastSegment=%%~H"
setlocal EnableDelayedExpansion
)
rem /* Creation of the target directory is now done once per source directory
rem (to check whether a directory exists, append `\*` rather than `\.`): */
if not exist "!secondLastSegment!\*" md "!secondLastSegment!"
endlocal
set "targetDir=%%G\image"
rem // Since `%%~xG` is going to be blank, `segment` will be empty and is therefore useless:
rem/ set "segment=%%~xG"
set /A "i=-1"
rem /* The `dir` should specify the file name mask(s) to exclude non-image files
rem (a sort order is now specified; hidden, system and linked items are now excluded): */
for /F "eol=| delims = " %%I in ('cd /D "%%G\image" ^&^& dir /B /A:-D-H-S-L /O:DN "*.jpg" "*.png"') do (
set /A "i+=1"
set "fileName=%%I"
rem // Variable `segment` is empty; use `%%~xI` instead:
set "fileExt=%%~xI"
setlocal EnableDelayedExpansion
set "newFileName=!secondLastSegment!_!i!!fileExt!"
rem/ ren "!targetDir!\!fileName!" "!newFileName!" && (
rem The target directory has already been created at this point.
rem/ move /Y "!targetDir!\!newFileName!" "!secondLastSegment!"
rem/ )
rem /* Instead of renaming and then moving, a single move operation suffices
rem (note that variable `segment` is empty, hence use `%%~xI` instead): */
> nul move /Y "!targetDir!\!fileName!" "!secondLastSegment!\!newFileName!"
rem // Clean up potentially empty sub-directory:
2> nul rd "!targetDir!"
endlocal
)
rem // Clean up potentially empty source directory:
2> nul rd "%%G"
)
endlocal
exit /B