在嵌套的for循环中执行字符串替换时遇到困难。

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

Struggling to perform string substitution within a nested for loop

问题

在Windows cmd shell批处理脚本中,我正在尝试创建一种机制,用于迭代一系列目录,这些目录的名称都以x.Year-Month-Day的形式命名,其中x是一个顺序值,从0递增到定义的限制。我想要移动/重命名这些目录,以使最后一个目录被删除,而其余的目录每次运行脚本时都增加一个索引。脚本不知道各个目录名称中的日期值将是什么。

具体如下:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM 设置工作变量:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
SET maxBackups=5

REM 开始工作...

REM 从maxBackups中减去1,因为索引从0开始:
SET /A limit=maxBackups-1

REM 删除索引等于限制的任何文件夹...
FOR /D %%f IN ("%localDest%\%limit%.*") DO rd /S /Q "%%f"

REM 遍历备份文件夹...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN ("%localDest%\!c!.*") DO (
        SET src=%localDest%\!c!.
        SET dst=%localDest%\%%i.
        REM *** 无法使此赋值工作! ***
        SET file=%%d:!src!=!dst!%%
        MOVE /Y "%%d" "!file!"
    )
)

REM 现在将当前数据文件夹复制到新的'0'索引备份文件夹中...
MD "%localDest%
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM 设置工作变量:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
SET maxBackups=5

REM 开始工作...

REM 从maxBackups中减去1,因为索引从0开始:
SET /A limit=maxBackups-1

REM 删除索引等于限制的任何文件夹...
FOR /D %%f IN ("%localDest%\%limit%.*") DO rd /S /Q "%%f"

REM 遍历备份文件夹...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN ("%localDest%\!c!.*") DO (
        SET src=%localDest%\!c!.
        SET dst=%localDest%\%%i.
        REM *** 无法使此赋值工作! ***
        SET file=%%d:!src!=!dst!%%
        MOVE /Y "%%d" "!file!"
    )
)

REM 现在将当前数据文件夹复制到新的'0'索引备份文件夹中...
MD "%localDest%\0.%date%"
ROBOCOPY "%source%\Data" "%localDest%\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V %1 %2 %3 %4 %5

:End
.
%date%"
ROBOCOPY "%source%\Data" "%localDest%
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM 设置工作变量:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
SET maxBackups=5

REM 开始工作...

REM 从maxBackups中减去1,因为索引从0开始:
SET /A limit=maxBackups-1

REM 删除索引等于限制的任何文件夹...
FOR /D %%f IN ("%localDest%\%limit%.*") DO rd /S /Q "%%f"

REM 遍历备份文件夹...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN ("%localDest%\!c!.*") DO (
        SET src=%localDest%\!c!.
        SET dst=%localDest%\%%i.
        REM *** 无法使此赋值工作! ***
        SET file=%%d:!src!=!dst!%%
        MOVE /Y "%%d" "!file!"
    )
)

REM 现在将当前数据文件夹复制到新的'0'索引备份文件夹中...
MD "%localDest%\0.%date%"
ROBOCOPY "%source%\Data" "%localDest%\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V %1 %2 %3 %4 %5

:End
.
%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V %1 %2 %3 %4 %5
:End

如上所示,我已经为您翻译了脚本的主要部分,包括注释。如果您有任何进一步的问题或需要进一步的帮助,请随时提问。

英文:

In a Windows cmd shell batch script, I am trying to craft a mechanism to iterate over a series of directories which all have their name in the form of x.Year-Month-Day, where x is a sequential value, from 0 up to a defined limit. I want to move/rename the directories, such that the last one is deleted, and the rest increment their index by one each time the script is run. The script doesn't know what the date values will be in the individual directory names.

To wit:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM Set work variables:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
SET maxBackups=5

REM Get to work... 

REM Deduct 1 from maxBackups b/c of 0-based count:
SET /A limit=maxBackups-1

REM Remove any folders indexed at the limit...
FOR /D %%f IN ("%localDest%\%limit%.*") DO rd /S /Q "%%f"    

REM Iterate through the backup folders...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN ("%localDest%\!c!.*") DO (
        SET src=%localDest%\!c!.
        SET dst=%localDest%\%%i.
        REM *** Can't get this assignment to work! ***
        SET file=%%%d:!src!=!dst!%%
        MOVE /Y "%%d" "!file!"
    )
)

REM Now copy the current data folder to a new '0' index backup folder...
MD "%localDest%
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM Set work variables:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
SET maxBackups=5
REM Get to work... 
REM Deduct 1 from maxBackups b/c of 0-based count:
SET /A limit=maxBackups-1
REM Remove any folders indexed at the limit...
FOR /D %%f IN ("%localDest%\%limit%.*") DO rd /S /Q "%%f"    
REM Iterate through the backup folders...
FOR /L %%i IN (%limit%,-1,1) DO (
SET /A c=%%i-1
FOR /D %%d IN ("%localDest%\!c!.*") DO (
SET src=%localDest%\!c!.
SET dst=%localDest%\%%i.
REM *** Can't get this assignment to work! ***
SET file=%%%d:!src!=!dst!%%
MOVE /Y "%%d" "!file!"
)
)
REM Now copy the current data folder to a new '0' index backup folder...
MD "%localDest%\0.%date%"
ROBOCOPY "%source%\Data" "%localDest%\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V %1 %2 %3 %4 %5
:End
.%date%" ROBOCOPY "%source%\Data" "%localDest%
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM Set work variables:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
SET maxBackups=5
REM Get to work... 
REM Deduct 1 from maxBackups b/c of 0-based count:
SET /A limit=maxBackups-1
REM Remove any folders indexed at the limit...
FOR /D %%f IN ("%localDest%\%limit%.*") DO rd /S /Q "%%f"    
REM Iterate through the backup folders...
FOR /L %%i IN (%limit%,-1,1) DO (
SET /A c=%%i-1
FOR /D %%d IN ("%localDest%\!c!.*") DO (
SET src=%localDest%\!c!.
SET dst=%localDest%\%%i.
REM *** Can't get this assignment to work! ***
SET file=%%%d:!src!=!dst!%%
MOVE /Y "%%d" "!file!"
)
)
REM Now copy the current data folder to a new '0' index backup folder...
MD "%localDest%\0.%date%"
ROBOCOPY "%source%\Data" "%localDest%\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V %1 %2 %3 %4 %5
:End
.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V %1 %2 %3 %4 %5 :End

As the comment indicates, I've tried innumerable permutations of the string-substitution, trying to create a new name for the directory with the modified index, but I cannot get the processor to do the substitution!

I've tried:

SET file=%%d:!src!=!dst!

Which produces just the three strings with the applicable punctuation, (and adding a trailing % doesn't do anything).

Prepending another % ...

SET file=%%%d:!src!=!dst!%

...produces a string that starts with a %, but only contains the values from !src! and !dst! separated by an equals sign, (=).

And, once again, adding another % to the end of the string, merely adds one to the end of the output.

I've tried innumerable combinations of % on the front and end of the assignment, but never seem to be able to get the substitution to work. I can't help but wonder if the colon from the path, (C:\), is messing with the parser. But surely a cmd shell batch processor, would be written with the understanding that paths including colons, would be exceedingly common things for such scripts to encounter, no?

I've been reading and re-reading articles here and around the web talking about how to do these things, but all the examples seem to be one-dimensional, (i.e. 'How to use delayed parsing', 'How to use variables inside loops', 'How to do string substitution' etc.). I haven't found anything demonstrating/trying to parse a string, (which contains a colon!), in real time, with value-substitution, from within a nested loop is discussed/explained/demonstrated. Trying to make it work has, thus far, proved entirely futile, and I'm at the point now of not knowing what to do/try next. What am I missing/not getting correct?

答案1

得分: 1

我会选择不同的方法:迭代备份文件夹(将它们的父文件夹作为工作文件夹可以简化操作,所以在执行之前,请使用 pushd 进入那里)。使用您的格式 <number><dot><somestring> 允许您使用 %%~n... 修饰符分离数字,并将 <dot><somestring> 作为 %%~x... 可用。

pushd %localdest%
for /f %%A in ('dir /ad /o-n /b ?.*') do (
   set /a c=%%~nA-1
   if !c! lss 0 goto :done
   ECHO move "%%A" "!c!%%~xA"
)
:done
popd

当输出满足您时,请删除 ECHO 关键字以实际启用 move 命令。

英文:

I would choose a different approach: iterating over the backup folders (having their parent folder as workingfolder simplifies things, so pushd there before). Having your nice format <number><dot><somestring> enables you to separate the number with the %%~n... modifier and have <dot><somestring> available as %%~x...

pushd %localdest%
for /f %%A in ('dir /ad /o-n /b ?.*') do (
   set /a c=%%~nA-1
   if !c! lss 0 goto :done
   ECHO move "%%A" "!c!%%~xA"
)
:done
popd

Remove the ECHO keyword when the output satisfies you to actually enable the move command.

答案2

得分: 0

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem 下面的设置用于目录名称,这是我用于测试的名称,故意包含空格以确保进程可以使用这些名称。这些需要根据您的情况进行更改。

SET "sourcedir=u:\your files\t w o"
SET /a maxfound=-1
SET /a maxindexminus1=17
:: 删除以#开头的变量
FOR /F "delims==" %%e IN ('set # 2^>Nul') DO SET "%%e="

FOR /f "tokens=1* delims=." %%b IN ('dir /b /ad "%sourcedir%\*" ') DO (
 IF %%b gtr %maxindexminus1% (ECHO RD /s /q "%%b.%%c"
 ) ELSE (
 IF %%b gtr !maxfound! SET /a maxfound=%%b
 SET /a nextindex=%%b+1
 SET "#%%b=ren "%sourcedir%\%%b.%%c" "!nextindex!.%%c""
 )
)

FOR /L %%e IN (%maxfound%,-1,0) DO ECHO !#%%e!

GOTO :EOF

我已为您提供了脚本的翻译。

英文:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION 
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.

SET "sourcedir=u:\your files\t w o"
SET /a maxfound=-1
SET /a maxindexminus1=17
:: remove variables starting #
FOR  /F "delims==" %%e In ('set # 2^>Nul') DO SET "%%e="

FOR /f "tokens=1*delims=." %%b IN ('dir /b /ad "%sourcedir%\*" ') DO (
 IF %%b gtr %maxindexminus1% (ECHO RD /s /q "%%b.%%c"
 ) ELSE (
 IF %%b gtr !maxfound! SET /a maxfound=%%b
 SET /a nextindex=%%b+1
 SET "#%%b=ren "%sourcedir%\%%b.%%c" "!nextindex!.%%c""
 )
)

FOR /L %%e IN (%maxfound%,-1,0) DO ECHO !#%%e!

GOTO :EOF

I've left the final create of 0.date out of the solution since the problem is how to change the index.

Noting that index.date and index+1.date may both exist (multiple backups made on the same day)

Read each directoryname in basic form (name only) into %%b,%%c, if %%b exceeds the maximum index, remove that directory (RD is echoed only for testing). Otherwise, retain the maximum index found in maxfound and record the required rename command in #%%b

Then loop through the rename commands in reverse, so that index+1.date is processed before index.date hence we cannot have an attempt to duplicate a name, even if the date part is the same (Again, ren is echoed only for testing)

答案3

得分: 0

由于所有的建议、评论和示例,我终于成功地拼凑出了解决我的问题的方法!

这实际上归结为没有意识到/理解 FOR 替代参数与字符串操作功能相关的 '普通' 环境变量不同。另外,了解 pushd/popd 也相当棒!

以下是我想出的简化版本:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM 设置工作变量:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
REM 注意:'maxBackups' 不能大于10!
SET maxBackups=5

REM 开始工作...

REM 减去 1,因为基于 0 的计数:
SET /A limit=maxBackups-1
PUSHD %localDest%

REM 删除索引到极限的任何/所有文件夹...
FOR /D %%f IN (".\%limit%.*") DO rd /S /Q "%%f"

REM 遍历现有的备份文件夹...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN (".\!c!.*") DO (
        SET folderSuffix=%%d
        MOVE /Y "%%d" ".\%%i!folderSuffix:~3!"
    )
)

REM 现在创建一个新的 '0' 索引备份文件夹...
MD ".
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM 设置工作变量:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
REM 注意:'maxBackups' 不能大于10!
SET maxBackups=5

REM 开始工作...

REM 减去 1,因为基于 0 的计数:
SET /A limit=maxBackups-1
PUSHD %localDest%

REM 删除索引到极限的任何/所有文件夹...
FOR /D %%f IN (".\%limit%.*") DO rd /S /Q "%%f"

REM 遍历现有的备份文件夹...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN (".\!c!.*") DO (
        SET folderSuffix=%%d
        MOVE /Y "%%d" ".\%%i!folderSuffix:~3!"
    )
)

REM 现在创建一个新的 '0' 索引备份文件夹...
MD ".\0.%date%"

REM ...并将当前数据文件夹的内容复制到其中!
ROBOCOPY "%source%\Data" ".\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V /J /TEE /COMPRESS %1 %2 %3 %4 %5

:End
POPD
.
%date%"
REM ...并将当前数据文件夹的内容复制到其中! ROBOCOPY "%source%\Data" ".
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM 设置工作变量:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
REM 注意:'maxBackups' 不能大于10!
SET maxBackups=5

REM 开始工作...

REM 减去 1,因为基于 0 的计数:
SET /A limit=maxBackups-1
PUSHD %localDest%

REM 删除索引到极限的任何/所有文件夹...
FOR /D %%f IN (".\%limit%.*") DO rd /S /Q "%%f"

REM 遍历现有的备份文件夹...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN (".\!c!.*") DO (
        SET folderSuffix=%%d
        MOVE /Y "%%d" ".\%%i!folderSuffix:~3!"
    )
)

REM 现在创建一个新的 '0' 索引备份文件夹...
MD ".\0.%date%"

REM ...并将当前数据文件夹的内容复制到其中!
ROBOCOPY "%source%\Data" ".\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V /J /TEE /COMPRESS %1 %2 %3 %4 %5

:End
POPD
.
%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V /J /TEE /COMPRESS %1 %2 %3 %4 %5
:End POPD

是的,我知道如果 maxBackups 超过 10 事情会非常错综复杂,但每个文件夹当前几乎有 20GB 的大小(而且还在增长),我真的不认为我们会保留比我们当前保留的更多(即大约 5 个)。我只是觉得使用这个变量可以在未来更容易地实现调整/更改,如果我们决定添加/删除一些的话(比如选择 7 来生成每天的图像,或者减少到 2 或 3 以节省空间,或类似的 🤷‍♀️)。

英文:

Thanks to all of the suggestions, comments and examples, I finally managed to cobble together a solution to my issue!

It really came down to not realising/understanding that FOR substitution parameters didn't function like 'normal' environment variables wrt the string manipulation features. Also, learning about pushd/popd was pretty awesome too!

Here's a pared-down version of what I came up with:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM Set work variables:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
REM NOTE: 'maxBackups' CANNOT be greater than 10!
SET maxBackups=5

REM Get to work... 

REM Deduct 1 from maxBackups b/c of 0-based count:
SET /A limit=maxBackups-1
PUSHD %localDest%

REM Remove any/all folders indexed at the limit...
FOR /D %%f IN (".\%limit%.*") DO rd /S /Q "%%f"    

REM Iterate through the existing backup folders...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN (".\!c!.*") DO (
        SET folderSuffix=%%d
        MOVE /Y "%%d" ".\%%i!folderSuffix:~3!"
    )
)

REM Now create a new '0' index backup folder...
MD ".
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM Set work variables:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
REM NOTE: 'maxBackups' CANNOT be greater than 10!
SET maxBackups=5
REM Get to work... 
REM Deduct 1 from maxBackups b/c of 0-based count:
SET /A limit=maxBackups-1
PUSHD %localDest%
REM Remove any/all folders indexed at the limit...
FOR /D %%f IN (".\%limit%.*") DO rd /S /Q "%%f"    
REM Iterate through the existing backup folders...
FOR /L %%i IN (%limit%,-1,1) DO (
SET /A c=%%i-1
FOR /D %%d IN (".\!c!.*") DO (
SET folderSuffix=%%d
MOVE /Y "%%d" ".\%%i!folderSuffix:~3!"
)
)
REM Now create a new '0' index backup folder...
MD ".\0.%date%"
REM ...and copy the current data folder's contents to it!
ROBOCOPY "%source%\Data" ".\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V /J /TEE /COMPRESS %1 %2 %3 %4 %5
:End
POPD
.%date%" REM ...and copy the current data folder's contents to it! ROBOCOPY "%source%\Data" ".
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM Set work variables:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
REM NOTE: 'maxBackups' CANNOT be greater than 10!
SET maxBackups=5
REM Get to work... 
REM Deduct 1 from maxBackups b/c of 0-based count:
SET /A limit=maxBackups-1
PUSHD %localDest%
REM Remove any/all folders indexed at the limit...
FOR /D %%f IN (".\%limit%.*") DO rd /S /Q "%%f"    
REM Iterate through the existing backup folders...
FOR /L %%i IN (%limit%,-1,1) DO (
SET /A c=%%i-1
FOR /D %%d IN (".\!c!.*") DO (
SET folderSuffix=%%d
MOVE /Y "%%d" ".\%%i!folderSuffix:~3!"
)
)
REM Now create a new '0' index backup folder...
MD ".\0.%date%"
REM ...and copy the current data folder's contents to it!
ROBOCOPY "%source%\Data" ".\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V /J /TEE /COMPRESS %1 %2 %3 %4 %5
:End
POPD
.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V /J /TEE /COMPRESS %1 %2 %3 %4 %5 :End POPD

..and yes, I'm aware that things will go tragically sideways if maxBackups exceeds 10, but each folder is nearly 20GB in size currently (and growing) and I don't genuinely forsee us ever keeping many more than the handful (i.e. ~5) that we're currently retaining. I just felt that using the variable made implementing tweaks/changes easier down the road if we ever decided we wanted to add/remove a few (i.e. going with 7 to have daily images, or down to 2 or 3 to save space, or something similar 🤷‍♀️).

huangapple
  • 本文由 发表于 2023年5月29日 13:47:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76354925.html
匿名

发表评论

匿名网友

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

确定