如何使用命令行参数字符串替换批处理文件中的字符串

16

我在cmd批处理文件中有以下内容:

for /f %%l in (%2) do (for %%f in (%%l) do copy "%%f" %1))

注意:该脚本的基本功能是读取一个文本文件,该文件以分号分隔的txt文件形式给出,路径由%2指定(例如包含c:\test1\file1.cs;d:\file2.js的文件),并将文件复制到由%1指定的目标文件夹。

我需要用传递给批处理文件的参数%4的值替换%1参数的字符串值x(该值也作为%3传递给批处理文件)。

例如:

if %1 = 'test replace x with y'
%3=x
%4=y

因此输出应为“用y替换y的测试”

如何使用Windows CMD批处理解释器实现这一点?


很难解析你的请求。为什么不能在需要它的两次使用中都使用%4,并忽略%3?......展示一些关于你所拥有的更详细的细节,我们应该能够帮助你。祝你好运。 - shellter
@shelter,问题在于%3和%4不同,这个批次是由外部MSBuild脚本调用的。替换字符串和被替换字符串不同,例如:我可能需要将FIND STRING(例如“test replace x x x”)中的“x”出现次数替换为“y”。谢谢... - DSharper
3个回答

62

首先,您需要将%1存储到变量中,然后才能进行替换。

基本上,替换的语法如下:

%<i>variable</i>:<i>str1</i>=<i>str2</i>%

这意味着:'用str2替换variable中的每个str1'。
在您的情况下,str1和str2都是参数,而不是字面字符串。直接使用上述模板可能会得到以下表达式:
%variable:%3=%4%
但这会使解析器困惑,因为它不知道%3和%4应该先评估。事实上,它会首先尝试评估%variable:%(并失败)。
在这种情况下的解决方案之一是使用一种称为“延迟”延迟评估的方法。基本上,您正在将评估变量的命令传递给CALL命令。原始命令转换为其'CALL版本'的过程如下:
ECHO %var% ==> CALL ECHO %%var%%。
请注意双%号。在解析时间,它们被评估为单个%。最终效果与原始命令ECHO %var%相同。

所以它的工作方式与原始命令相同(这很好),我们在这里获得的是更晚的评估时间,我的意思是最终评估,当变量实际上被其值替换时。了解到这种效果后,我们可以构造我们的表达式,使%3%4首先被评估,然后才是整个结果表达式。具体来说,就像这样:

%%variable:%3=%4%%

第一次解析后,该表达式将变成以下内容:

%variable:x=y%

这将再次进行解析,输出将是variable修改后的内容。

为更好地说明,这里有一个简单的工作示例:

SET "output=%1"
CALL SET output=%%output:%3=%4%%
ECHO %output%

更新

还有一种方法可以完成同样的事情,我应该先提到它。

Windows命令行支持一个真正的延迟扩展。使用起来更简单,但也有一些注意事项。

首先,如何使用它。延迟扩展的语法是!var!,而不是立即扩展的%var%(立即扩展仍然有效,并且可以与延迟扩展语法一起使用)。

可能在您的脚本中!var!不会起作用,直到您使用以下命令启用语法:

SETLOCAL EnableDelayedExpansion
< p > ENDLOCAL 命令关闭延迟扩展语法在命令 shell 中有效并被解释的块。

以上示例脚本可以重写如下:

SET "output=%1"
SETLOCAL EnableDelayedExpansion
SET output=!output:%3=%4!
ECHO !output!
ENDLOCAL

对于SET output=!output:%3=%4!命令的工作原理:

  • %3%4立即进行评估,即在解析时替换为xy

  • 命令变成这样:SET output=!output:x=y!

  • 命令即将执行 - 评估!表达式(x被替换为y);

  • 命令被执行 - 修改output变量。

现在来谈谈注意事项。首先要记住的是,!成为语法的一部分,并且每当遇到它时都会被消耗和解释。因此,您需要在想要将其用作字面值的位置进行转义(例如^!)。

另一个需要注意的是SETLOCAL/ENDLOCAL块的主要影响。问题在于,这种块内环境变量的所有更改都是本地的。退出块(执行ENDLOCAL)时,变量将设置为进入之前的值(执行SETLOCAL之前的值)。这意味着对于您而言,output的更改值仅在您必须启动SETLOCAL块以使用延迟扩展的情况下才有效。如果您只需要修改值然后立即使用它,则可能不会出现问题,但您应该记住这一点以备将来之需。
注意:根据jeb的评论,您可以使用以下技巧保存修改后的值并离开SETLOCAL块:
ENDLOCAL & SET "output=%output%"
& 运算符只是在同一行上放置命令时的分隔符。它们按照指定的顺序一个接一个地执行。问题在于,在解析该行时,SETLOCAL块尚未离开,因此 %output% 求值为修改后的值,仍然有效。但是赋值实际上是在 ENDLOCAL 之后执行的,即在离开块之后执行。因此,您实际上是在离开块后存储修改后的值,从而保留了更改。(感谢jeb!)

更多信息:

  1. 关于延迟扩展:
在子字符串替换方面:

+1,但是为什么你展示了“懒惰”的延迟评估,而不是(更简单的)延迟扩展呢? - jeb
@jeb:感谢提问!我不知道为什么,在我进行测试时(在Win7上),这种写法并没有起作用:set "output=%1"; setlocal EnableDelayedExpansion; set "output=!output:%3=%4!"; endlocal; echo %output%。奇怪的是,!output:%3=%4!在ECHO中可以正常工作,但是在SET中却无效,变量根本就不会更改。 - Andriy M
1
但这没关系,因为在setlocal块中更改输出变量后,在endlocal之后,输出变量将恢复其原始值。这不是Win7的问题。 - jeb
1
你可以使用( endlocal & set "output=%output%")来离开代码块并保留修改后的值。 - jeb
1
@dgo:很奇怪。我刚在Win 7中测试了你的第一种方法,对我来说似乎正常工作(截图中的“ECHO test--test”行在脚本中是“ECHO %var2%”)。也许我错过或误解了什么。 - Andriy M
显示剩余4条评论

1
我已经在Windows 7批处理文件中尝试了以下代码:

SET output=%1
CALL SET output=%output:unsigned=signed%
CALL SET output=%output:.apk=-aligned.apk%

它可以工作!


但这并没有解决问题。问题是关于用未知参数替换%3%4中的字符串。 - jeb

-1

如果您需要在一个字符串中替换多个参数,只需使用 "for /f" 设置变量,并进行前面的替换,像这样:

SET "output1=%1"
CALL SET output1=%%output1:%3=%4%%
for /f "tokens=1" %%a in ('echo %output1%') do set output2=%%a
CALL SET output2=%%output2:%5%6%
for /f "tokens=1" %%a in ('echo %output2') do set output3=%%a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接