【问题标题】:How to expand two local variables inside a for loop in a batch file如何在批处理文件的for循环内扩展两个局部变量
【发布时间】:2016-12-12 20:46:16
【问题描述】:

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

最初,这是为了复制在 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!=!!

但两者都行不通。

我在这些方面做错了什么,还是有一种优雅的方式(因此不使用临时文件)来完成这个?

谢谢。

【问题讨论】:

    标签: windows loops batch-file for-loop


    【解决方案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 的值不得以~* 开头,且不得包含=。它也不能以!开头。

    【讨论】:

      【解决方案2】:
      FOR %%y IN ("!dest_file_full!") DO set "dest_file_dir=%%~dpy"
      

      应该将您的目标驱动器+路径+终端\ 转换为dest_file_dir

      不用说,毫无疑问,:: 形式的注释不应该在块中使用。

      【讨论】:

        【解决方案3】:

        (代码块)中的字符串操作有时会令人困惑。我个人更喜欢将这些放入一个潜艇中。此批处理会暂时关闭提示并打开 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%"
        

        尤其是第二组我不清楚,更多真实的变量应该会有所帮助。

        【讨论】:

          猜你喜欢
          • 2013-08-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多