如何在批处理文件的for循环内扩展两个局部变量 [英] How to expand two local variables inside a for loop in a batch file

查看:613
本文介绍了如何在批处理文件的for循环内扩展两个局部变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

最初,此操作旨在复制在一定范围内的Git提交中更改的文件,但是由于我发现了一种更简单的方法(首先使用C#脚本",然后使用

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.

这是 file_list.txt 的示例:

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

这是我到目前为止的 .bat 文件:

This is my .bat file so far:

@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 不会一步扩展两个局部变量.

Notice the commented line 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.

我已经尝试过管道...

I already tried a pipe...

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

或使用call:

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

但是两者都不行.

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

Am I doing something wrong in these, or is there an elegant way (hence without using a temp file) to accomplish this?

谢谢.

推荐答案

此答案仅阐述了扩展嵌套变量"的问题:

简而言之,请使用:

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


转义^!或使用call!!不能在延迟扩展的情况下扩展嵌套变量.


Escaping ^! or using call and !! does not work to expand nested variables with delayed expansion.

管道在为双方初始化使用各自独立环境的新cmd实例时无济于事.

Pipes cannot help either as they initialise new cmd instances for both sides, which use their own separate environments.

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

Let us take a look at nested variables using immediate expansion first:

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

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

call introduces a second parsing phase, which is required to resolve the nesting. The call command receives a partially expanded command line, like this (supposing %dest_file_filename% expands to config.php, for example):

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.

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

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 =! 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:

根据文章 Windows命令解释器(CMD.EXE)如何解析脚本?call引入了第二篇解析阶段,但这不包括延迟扩展,而仅包括立即扩展;因此延迟扩展只能在第一个解析阶段完成.

According to the post How does the Windows Command Interpreter (CMD.EXE) parse scripts?, call introduces a second parsing phase, but this does not include delayed expansion but immediate expansion only; 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!=%%"

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

So the call command receives the following partially expanded command line:

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

再次构成立即扩展的子字符串替换语法.

Again this constitutes the sub-string replacement syntax with immediate expansion.

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

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 =! In addition, if present, " could cause trouble too (but such are also not allowed in file names)!

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

An alternative method to introduce a second parsing phase needed for expanding nested variables is to use a for loop, like this:

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

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

Here, !dest_file_filename! is expanded when the entire for /F loop is parsed; 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中,则可能会引起麻烦;但是,这会导致!^出现问题(必须先在^前面进行转义).
dest_file_filename的值仍不能以~*开头,并且仍然不能包含=.它也不能以!开头.

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 ^).
The value of dest_file_filename must still not begin with ~ or * and must still not contain =. It must also not begin with !.

这篇关于如何在批处理文件的for循环内扩展两个局部变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆