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

时间:2016-12-12 20:46:16

标签: windows loops batch-file for-loop

我试图编写一个脚本,该脚本应从文件列表中读取并按照相同的结构将每个项目复制到新位置。

最初,这是为了复制在Git提交范围内更改的文件,但是因为我找到了一种更简单的方法(首先是C#"脚本",然后是this),现在我只是为了学习而这样做。

这是 file_list.txt 的示例:

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

到目前为止,这是我的 .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

注意注释行set dest_file_dir=(!dest_file_full - !dest_file_filename)。这是伪代码。我需要在这里使用变量替换,但 cmd.exe 不会一步扩展两个局部变量。

我已经尝试过管道......

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

或使用call

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

但两者都不会起作用。

我在这些方面做错了什么,或者是否有一种优雅的方式(因此没有使用临时文件)来实现这一目标?

感谢。

3 个答案:

答案 0 :(得分:1)

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

应将您的目标广告系列+路径+终端\转换为dest_file_dir

毫无疑问,毫无疑问,::形式的评论不应该被用在一个区块内。

答案 1 :(得分:1)

这个答案详细阐述了扩展"嵌套变量"仅

简而言之,请使用:

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

转义^!或使用call!!无法扩展具有延迟扩展的嵌套变量。

管道无法帮助,因为它们初始化双方的新cmd实例,这两个实例使用自己独立的环境。

让我们先看一下使用立即扩展的嵌套变量:

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

call引入了第二个解析阶段,这是解析嵌套所必需的。 call命令接收部分扩展的命令行,例如这样(假设%dest_file_filename%扩展为config.php):

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

这只是具有立即扩展的正常子字符串替换语法,在第二个解析阶段会扩展。

注意:如果dest_file_filename的值以%~*开头,则此方法失败(但这是一个无论如何,禁止文件名的字符),或者如果它包含=!此外,如果存在,"也可能导致麻烦(但文件名中也不允许这样做)!

现在让我们看看类似的方法,但启用了延迟扩展:

根据帖子How does the Windows Command Interpreter (CMD.EXE) parse scripts?call引入了第二个解析阶段,但这不包括延迟扩展但仅包括立即扩展;所以延迟扩展只在第一个解析阶段完成。

与使用立即扩展的上述解决方案类似,我们需要确保在第一个解析阶段扩展子字符串替换语法的搜索和替换字符串,并且整个表达式由第二个解析器处理;所以让我们用这个:

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

因此call命令接收以下部分扩展的命令行:

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

这再次构成了具有立即扩展的子字符串替换语法。

注意:如果dest_file_filename的值以%~*开头,则此方法失败(但这是一个无论如何,禁止文件名的字符),或者如果它包含=!此外,如果存在,"也可能导致麻烦(但文件名中也不允许这样做)!

引入扩展嵌套变量所需的第二个解析阶段的另一种方法是使用for循环,如下所示:

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

这里,在解析整个!dest_file_filename!循环时展开for /F;第二个解析阶段适用于循环体,并接收dest_file_filename的值(例如,config.php)而不是%%K

这种方法的优点是可以完全避免立即扩展,如果"的值dest_file_filename出现!,可能会造成麻烦。但是,这会引入^^的问题(需要通过dest_file_filename前面的文件进行转义)。
~的值仍然不得以*=开头,且仍不得包含!。它也必须不以list1=['a','b','d','b','c','d','e','f'] list2=['a','b','d','d'] 开头。

答案 2 :(得分:0)

(代码块)中的字符串操作有时会令人困惑。我个人更喜欢将这些放入子中。此批次暂时关闭提示和Echo on以更好地输出您的更改:

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

特别是第二组对我来说不清楚,更多真正的变量应该有所帮助。