我有很多这种格式的文件:
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 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
命令; plain for
或 for /D
比 for /F
更简单且工作速度更快。for /D %%d in (*_*) do
命令处理所有至少带有一个下划线的文件夹;我不明白你为什么使用 findstr
和复杂的正则表达式......~Name
和 ~eXtension
FOR 命令参数修饰符提取 第二个姓氏。
根据您提供的信息,这对您有帮助吗?
@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
在应用于真实数据之前,始终验证测试目录。
查找包含
_
的每个子目录。通过将 _
替换为空格并使用 _
来查找倒数第二个 for
分隔的元素。对于 ssubdirectory\image 中的每个 *.jpg
,尝试使其目标(2>nul 抑制诸如“目录已存在”之类的错误消息),然后尝试在目标目录中查找第一个可用名称(因为不能保证杂散文件目的地中尚不存在)并进行移动。
您的代码中有几个问题,我想消除它们:
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