使用批处理脚本批量重命名多个文件

huangapple go评论94阅读模式
英文:

Renaming multiple files using Batch script

问题

我理解你的要求,以下是翻译的部分:

我有许多以这种格式命名的文件:

```none
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
英文:

I have a lot of files in this format:

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

I'd like to move and rename these files using a substring of the absolute file path and remove the original files so that the results will look something like the below, starting with 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

Note that the number of tokens in the folder with the underscores can be any length.

The following attempt, would only give me the 2nd token starting from the front. Using "Tokens=-2" to get the 2nd last token wouldn't output anything.

@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

Then I used different logic by trying two for loops to get the last token, and then the 2nd last token, but it didn't work.

@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

答案1

得分: 1

这应该可以工作:

@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 命令;普通的 forfor /Dfor /F 更简单,而且速度更快。
  • for /D %%d in (*_*) do 命令处理至少有一个下划线的所有文件夹;我不明白为什么要使用复杂的正则表达式来使用 findstr...
  • 查看我的技巧,使用 ~Name~eXtension FOR 命令参数修改器提取_倒数第二个名称_。
  • 如果您只处理每个源文件夹一次,那么只需要创建相应的目标文件夹_一次_。您不需要在_每个文件_中检查是否存在这样的文件夹。如果目标文件夹以前可能存在,您只需为每个源文件夹检查一次即可...
英文:

This should work:

@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"
   )
)

Your examples have a lot of unneeded or redundant code...

  • In general, it is not necessary to use a for /F command when you just want to process files or folders; plain for or for /D are simpler and works faster than for /F.
  • A for /D %%d in (*_*) do command process all folders with at least one underscore; I don't see why you use a findstr with a complicated regex...
  • See my trick to extract the second last name using ~Name and ~eXtension FOR command parameter modifiers.
  • If you process each source folder once you just need to create the corresponding target folder once. You don't need to check if such a folder exist in each file. If the target folder may previously exist, you can check it just once per source folder...

答案2

得分: 0

基于您提供的信息,这是否对您有所帮助?

@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

请注意,您可能有空目录需要清理!

英文:

Based upon your provided information, does this help you out?

@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

Please be aware that you may have empty directories which you may wish to clean up too!

答案3

得分: 0

@ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION

SET "sourcedir=u:\images"

FOR /f %%e IN ('DIR /b /ad "%sourcedir%*_*"') DO (
 rem %%e has first-level 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
英文:
@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

Always verify against a test directory before applying to real data.

Find each subdirectory that contains _. Find the second-last _-separated element by replacing _s with spaces and using a for. For each *.jpg in the ssubdirectory\image, try to make its destination (2>nul suppresses error messages like "Directory already exists") then try to find the first available namein the destination directory (since there is no guarantee that stray files don't already exist in the destination) and make the move.

答案4

得分: 0

以下是修复了问题的代码:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // 使用 `pushd`/`popd` 通常不需要在使用 `setlocal/`endlocal` 时:
cd /D "%~dp0images" || exit /B

for /F "eol=| delims=" %%G in ('
    rem/ // `findstr` 过滤器太强了,不再需要 ^(简单的 `dir` 足够^)^^! ^& ^
        2>nul dir /B /A:D-H-S-L "*_*"
') do (
    set "dirName=%%G"
    set "lastSegment="
    rem // 切换到延迟扩展以仅在必要时启用它以避免丢失 `!` 符号:
    setlocal EnableDelayedExpansion
    for %%H in ("!dirName:_" "!"^) do for %%J in ("!lastSegment!"^) do (
        endlocal
        set "secondLastSegment=%%~J"
        set "lastSegment=%%~H"
        setlocal EnableDelayedExpansion
    )
    rem /* 现在每个源目录仅创建一次目标目录
    rem    (检查目录是否存在时,请附加 `\*` 而不是 `\.`): */
    if not exist "!secondLastSegment!\*" md "!secondLastSegment!"
    endlocal
    set "targetDir=%%G\image"
    rem // 由于 `%%~xG` 将为空白,`segment` 将为空,因此没有用:
    rem/ set "segment=%%~xG"
    set /A "i=-1"
    rem /* `dir` 现在应指定文件名掩码以排除非图像文件
    rem    (现在指定了排序顺序;隐藏、系统和链接项现在被排除): */
    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 // 变量 `segment` 为空;请改用 `%%~xI`:
        set "fileExt=%%~xI"
        setlocal EnableDelayedExpansion
        set "newFileName=!secondLastSegment!_!i!!fileExt!"
        rem/ ren "!targetDir!\!fileName!" "!newFileName!" & (
            rem 目标目录在此时已创建。
            rem/ move /Y "!targetDir!\!newFileName!" "!secondLastSegment!"
        rem/ )
        
        
        rem /* 不必先重命名然后移动,单个移动操作就足够了
        rem    (请注意变量 `segment` 为空,因此请改用 `%%~xI`):
        > nul move /Y "!targetDir!\!fileName!" "!secondLastSegment!\!newFileName!"
        rem // 清理潜在的空子目录:
        2>nul rd "!targetDir!"
        endlocal
    )
    rem // 清理潜在的空源目录:
    2>nul rd "%%G"
)

endlocal
exit /B

如果需要进一步的解释或帮助,请告诉我。

英文:

<!-- language-all:lang-cmd -->

There are several issues in your code, which I want to eliminate:

  • Your findstr filter is too strict, hence it would only let the last two images in your example list be processed. Let me recommend to completely remove it and use the directory mask *_* instead.
  • The target directory does not need to be attempted to become created per each image file; one attempt per source directory is sufficient.
  • Checking a directory for existence by if exist results in a wrong result when appending \. to the path (when it points to a file rather than a directory, it is also reported as existing); instead \* should be appended, so files are reported as not existing.
  • You should specify a file name mask like *.jpg and/or *.png for the image files to be processed in order to filter out unintended ones.
  • Perhaps you should specify a sort order for the image files to be processed, because otherwise, the file system determines the order, which may not be what you expect.
  • Variable segment will be empty, because %%~xG is going to be blank (based on your examples). You most probably meant %%~xI.
  • Although technically working, first renaming the current image file and then moving it into another sub-directory is not necessary; a single movement operation is enough.
  • After movement of image files, empty sub-directories may remain, which should become cleaned up.
  • When using setlocal for environment localisation, usage of pushd is mostly not necessary, because setlocal comprises the current working directory also. Remember that lack of popd may leave unwanted pushd stack entries behind, in contrast to setlocal, because endlocal always becomes implicitly executed when a batch script is terminated.
  • Delayed variable expansion should not be globally enabled, because file/directory names containing ! or ^ in their names might cause issues.

Here is your code with said issues fixed (without touching the intrinsic methodology):

@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Usage of `pushd`/`popd` is usually not needed when using `setlocal/`endlocal`:
cd /D &quot;%~dp0images&quot; || exit /B
for /F &quot;eol=| delims=&quot; %%G in (&#39;
rem/ // The `findstr` filter was too strong and is unnecessary ^(a simple `dir` is enough^)^^! ^&amp; ^
2^&gt; nul dir /B /A:D-H-S-L &quot;*_*&quot;
&#39;) do (
set &quot;dirName=%%G&quot;
set &quot;lastSegment=&quot;
rem // Toggle delayed expansion to have it enabled only when necessary to not lose `!`-symbols:
setlocal EnableDelayedExpansion
for %%H in (&quot;!dirName:_=&quot; &quot;!&quot;) do for %%J in (&quot;!lastSegment!&quot;) do (
endlocal
set &quot;secondLastSegment=%%~J&quot;
set &quot;lastSegment=%%~H&quot;
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 &quot;!secondLastSegment!\*&quot; md &quot;!secondLastSegment!&quot;
endlocal
set &quot;targetDir=%%G\image&quot;
rem // Since `%%~xG` is going to be blank, `segment` will be empty and is therefore useless:
rem/ set &quot;segment=%%~xG&quot;
set /A &quot;i=-1&quot;
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 &quot;eol=| delims=&quot; %%I in (&#39;cd /D &quot;%%G\image&quot; ^&amp;^&amp; dir /B /A:-D-H-S-L /O:DN &quot;*.jpg&quot; &quot;*.png&quot;&#39;) do (
set /A &quot;i+=1&quot;
set &quot;fileName=%%I&quot;
rem // Variable `segment` is empty; use `%%~xI` instead:
set &quot;fileExt=%%~xI&quot;
setlocal EnableDelayedExpansion
set &quot;newFileName=!secondLastSegment!_!i!!fileExt!&quot;
rem/ ren &quot;!targetDir!\!fileName!&quot; &quot;!newFileName!&quot; &amp;&amp; (
rem The target directory has already been created at this point.
rem/ move /Y &quot;!targetDir!\!newFileName!&quot; &quot;!secondLastSegment!&quot;
rem/ )
rem /* Instead of renaming and then moving, a single move operation suffices
rem    (note that variable `segment` is empty, hence use `%%~xI` instead): */
&gt; nul move /Y &quot;!targetDir!\!fileName!&quot; &quot;!secondLastSegment!\!newFileName!&quot;
rem // Clean up potentially empty sub-directory:
2&gt; nul rd &quot;!targetDir!&quot;
endlocal
)
rem // Clean up potentially empty source directory:
2&gt; nul rd &quot;%%G&quot;
)
endlocal
exit /B

huangapple
  • 本文由 发表于 2023年4月10日 19:52:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75976856.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定