简体   繁体   English

如何在批处理文件的for循环内扩展两个局部变量

[英]How to expand two local variables inside a for loop in a batch file

I'm trying to write a script which should read from a list of files and copy each item to a new location, following the same structure. 我正在尝试编写一个脚本,该脚本应从文件列表中读取并将每个项目复制到新位置,并遵循相同的结构。

Originally, this was intended to copy files which were changed within a range of Git commits, but since I found an easier way (first a C# "script", then this ), now I'm only doing it in the interest of learning. 最初,此操作旨在复制在一定范围内的Git提交中更改的文件,但是由于我找到了一种更简单的方法(首先是C#“脚本”,然后是this ),现在我只是出于学习目的而这样做。

This is an example of file_list.txt : 这是file_list.txt的示例:

config.php
repository\file picker.php
user\edit.php

This is my .bat file so far: 到目前为止,这是我的.bat文件:

@echo off

setlocal enabledelayedexpansion

set "source=input dir"
set "target=output dir"

for /f "tokens=* usebackq" %%A in ("file_list.txt") do (
    set "FILE=%%A"
    set "dest_file_full=%target%\!FILE:%source%=!"
    set "dest_file_filename=%%~nxA"
    ::set dest_file_dir=(!dest_file_full - !dest_file_filename)
    if not exist "!dest_file_dir!" (
        md "!dest_file_dir!"
    )
    set "source_file_full=%source%\!FILE:%source%=!"
    copy "!source_file_full!" "!dest_file_dir!"
)
pause

Notice the commented line set dest_file_dir=(!dest_file_full - !dest_file_filename) . 注意注释行set dest_file_dir=(!dest_file_full - !dest_file_filename) This is pseudo-code. 这是伪代码。 I need to use variable substitution in here, but cmd.exe won't expand two local variables at one step. 我需要在此处使用变量替换,但cmd.exe不会一步扩展两个局部变量。

I already tried a pipe... 我已经试过了...

echo set dest_file_dir=^!dest_file_full:!dest_file_filename!=^! | cmd

or using call : 或使用call

call set dest_file_dir=!!dest_file_full:!dest_file_filename!=!!

But neither will work. 但两者都行不通。

Am I doing something wrong in these, or is there an elegant way (hence without using a temp file) to accomplish this? 我是否在这些方面做错了,还是有一种优雅的方法(因此不使用临时文件)来完成此任务?

Thanks. 谢谢。

FOR %%y IN ("!dest_file_full!") DO set "dest_file_dir=%%~dpy"

should get your destination drive+path+terminal \\ into dest_file_dir . 应该将您的目标drive + path + terminal \\放入dest_file_dir

needless to say, no doubt, the :: form of comment shouldn't be used in a block. 不用说,毫无疑问, ::注释形式不应该在一个块中使用。

This answer elaborates on expanding "nested variables" only: 此答案仅阐述了扩展“嵌套变量”:

In short, use this: 简而言之,使用以下命令:

call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%"

Escaping ^! 转义^! or using call and !! 或使用call!! does not work to expand nested variables with delayed expansion. 无法通过延迟扩展来扩展嵌套变量。

Pipes cannot help either as they initialise new cmd instances for both sides, which use their own separate environments. 管道在为双方初始化新的cmd实例时都无济于事,后者使用各自独立的环境。


Let us take a look at nested variables using immediate expansion first: 让我们先来看一下使用立即展开的嵌套变量:

call set "dest_file_dir=%%dest_file_full:%dest_file_filename%=%%"

call introduces a second parsing phase, which is required to resolve the nesting. call引入了第二个解析阶段,这是解决嵌套所必需的。 The call command receives a partially expanded command line, like this (supposing %dest_file_filename% expands to config.php , for example): call命令接收部分扩展的命令行,例如(假设%dest_file_filename%扩展为config.php ):

set "dest_file_dir=%dest_file_full:config.php=%"

This is nothing but the normal sub-string replacement syntax with immediate expansion, which is expanded during the said second parsing phase. 这只是具有立即扩展的普通子字符串替换语法,该语法在所述第二解析阶段期间被扩展。

Note: This approach fails in case the value of dest_file_filename begins with % , ~ , or * (but this is a forbidden character for file names anyway), or if it contains = ! 注意:如果dest_file_filename的值以%~*开头(但是无论如何这都是文件名的禁止字符)或包含= !,则此方法将失败。 In addition, if present, " could cause trouble too (but such are also not allowed in file names)! 另外,如果存在"也可能会引起麻烦(但文件名中也不允许这样)!


Now let us check out a similar approach but with delayed expansion enabled: 现在让我们看看一种类似的方法,但是启用了延迟扩展:

According to the post How does the Windows Command Interpreter (CMD.EXE) parse scripts? 根据文章Windows命令解释器(CMD.EXE)如何解析脚本? , call introduces a second parsing phase, but this does not include delayed expansion but immediate expansion only; call引入了第二个解析阶段,但这不包括延迟扩展,而仅包括立即扩展; so delayed expansion is only done during the first parsing phase. 因此延迟扩展仅在第一个解析阶段完成。

Similar to the above solution using immediate expansion, we need to make sure that the search and replace strings of the sub-string replacement syntax are expanded during the first parsing phase, and that the overall expression is handled by the second one; 与上述使用立即扩展的解决方案类似,我们需要确保在第一个解析阶段扩展了子字符串替换语法的搜索和替换字符串,并且整个表达式由第二个解析处理。 so let us use this: 所以让我们使用这个:

call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%"

So the call command receives the following partially expanded command line: 因此, call命令将收到以下部分扩展的命令行:

set "dest_file_dir=%dest_file_full:config.php=%"

Again this constitutes the sub-string replacement syntax with immediate expansion. 同样,这构成了立即扩展的子字符串替换语法。

Note: This approach fails in case the value of dest_file_filename begins with % , ~ , or * (but this is a forbidden character for file names anyway), or if it contains = ! 注意:如果dest_file_filename的值以%~*开头(但是无论如何这都是文件名的禁止字符)或包含= !,则此方法将失败。 In addition, if present, " could cause trouble too (but such are also not allowed in file names)! 另外,如果存在"也可能会引起麻烦(但文件名中也不允许这样)!


An alternative method to introduce a second parsing phase needed for expanding nested variables is to use a for loop, like this: 引入扩展嵌套变量所需的第二个解析阶段的另一种方法是使用for循环,如下所示:

for /F delims^=^ eol^= %%K in ("!dest_file_filename!") do set "dest_file_dir=!dest_file_full:%%K=!"

Here, !dest_file_filename! 在这里, !dest_file_filename! is expanded when the entire for /F loop is parsed; 在解析整个for /F循环时扩展; the second parsing phase applies to the body of the loop and receives the value of dest_file_filename (for instance, config.php ) instead of %%K . 第二个解析阶段适用于循环的主体,并接收dest_file_filename的值(例如config.php )而不是%%K

The advantage of this method is that immediate expansion is avoided completely, which could cause trouble in case " could occur in the value of dest_file_filename ; however, this introduces problems with ! and ^ (they need to be escaped by preceding with ^ ). 此方法的优点是完全避免了立即扩展,如果在" dest_file_filename的值中可能出现"情况下可能会引起麻烦;但是,这会带来!^问题(它们必须通过在^前面进行转义)。
The value of dest_file_filename must still not begin with ~ or * and must still not contain = . dest_file_filename的值仍不能以~*开头,并且也不能包含= It must also not begin with ! 它也不能以! .

String manipulation in (code blocks) gets sometimes confusing. (代码块中的)字符串操作有时会造成混乱。 I personally prefer to put these into a sub. 我个人更喜欢将它们放在一个子目录中。 This batch temporarily shuts off the prompt and Echo on to have a better output of your alterations: 该批处理暂时关闭了提示,并打开Echo可以更好地输出更改:

@echo off
setlocal enabledelayedexpansion
Set MyPrompt=%Prompt%
Prompt $S
Echo on
set "source=input dir"
set "target=output dir"
for /f "tokens=* usebackq" %%A in ("file_list.txt") do call :sub "%%A"
pause
Prompt %MyPrompt%
Goto :Eof

:Sub
set "FILE=%~1"
set "dest_file_full=%target%\!FILE:%target%=!"
set "dest_file_filename=%~nx1"
set "dest_file_dir=!dest_file_full:%dest_file_filename%=!"
Rem if not exist "%dest_file_dir%" md "%!dest_file_dir%"
set "source_file_full=%source%\%FILE%"
@Echo copy "%source_file_full%" "%dest_file_dir%"

Especially the second set is unclear to me, more real vars should help. 特别是第二组对我来说还不清楚,更多的真实变量应该有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM