使用批处理文件编辑字符串中的单词

问题描述 投票:0回答:3

这是我到目前为止所写的:

for /l %%x in (Veronica, Nils, Mike, Tom) do (
set word=%%x
set str="My name is Name"
call set str=%%str:Name=%word%%%
echo %str% >> names.txt
)

不幸的是,唯一的输出是:

ECHO is on.
ECHO is on.
ECHO is on.

在无限循环中。

关于我哪里出错的任何想法/

windows batch-file cmd
3个回答
0
投票

Windows命令处理器在命令块中替换所有出现的%variable%,该命令块以(开头,并在使用命令块执行命令之前以引用环境变量的当前值匹配)结束。请参阅How does the Windows Command Interpreter (CMD.EXE) parse scripts? cmd.exe的命令块处理可以通过在命令提示符窗口中运行批处理文件而不是双击批处理文件,@echo off被删除或修改为@echo ON或临时注释掉,直到批处理文件按预期工作。

在命令提示符窗口中运行set /?的帮助输出解释了IF和FOR示例何时以及如何使用delayed expansion,这是在同一命令块中定义或修改的命令块中访问环境变量值的首选方法。

另一个解决方案是使用语法%%variable%%引用环境变量,并使用命令CALL强制cmd.exe对此命令行进行双重解析。在这种情况下,%%在将整个命令块解析为引用环境变量两侧的%时进行修改,稍后在执行命令CALL时,剩余的%variable%再次被解析并被引用变量的当前值替换。

在已发布的批处理代码中,环境变量str未在上面使用命令块FOR命令行定义。出于这个原因,echo %str% >> names.txt正在解析由cmd.exe修改的整个命令块到echo >> names.txt,然后才执行FOR,因此ECHO只输出当前状态的三倍。

命令行call set str=%%str:Name=%word%%%也不起作用,因为在使用命令块的FOR命令行之前没有定义word,因此%word%已被替换为空,导致命令行call set str=%str:Name=%最终在循环执行时被执行三次。

下一个错误是对此循环使用FOR选项/L。在命令提示符窗口中运行for /?的帮助输出解释了for /L

FOR /L %variable IN (start,step,end) DO command [command-parameters]

    The set is a sequence of numbers from start to end, by step amount.
    So (1,1,5) would generate the sequence 1 2 3 4 5 and (5,-1,1) would
    generate the sequence (5 4 3 2 1)

在阅读此帮助后,应该清楚/L不是此任务的正确选项。

下一个错误是在解析命令行时使用命令SET或cmd.exe直接执行的字符串替换始终应用不区分大小写。分配给环境变量str的字符串包含nameName,它们都替换为字符串替换。这不是真的想要的。

然后使用set variable="value"或使用set "variable=value"有很大的不同。后者是更好的,正如Why is no string output with 'echo %var%' after using 'set var = text' on command line?的答案中详细解释的那样

最后,重定向操作符>>的空格字符也由命令ECHO输出,从而在文件中产生尾随空格。因此,重定向运算符>>不应该用字符串空格分隔以写入文件。

该示例的第一个工作代码使用延迟环境变量扩展:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
del names.txt 2>nul
set "str=My name is #name#"
for %%I in (Veronica, Nils, Mike, Tom) do echo !str:#name#=%%~I!>>names.txt
endlocal

该示例的第二个工作代码使用CALL技巧:

@echo off
del names.txt 2>nul
set "str=My name is #name#"
for %%I in (Veronica, Nils, Mike, Tom) do call echo %%str:#name#=%%~I%%>>names.txt

要了解使用的命令及其工作方式,请打开命令提示符窗口,执行以下命令,并完全阅读为每个命令显示的所有帮助页面。

  • call /?
  • del /?
  • echo /?
  • endlocal /?
  • for /?
  • setlocal /?
  • set /?

另请参阅有关Using command redirection operators的Microsoft文章。


0
投票

你可能想看看从cmdline delayedexpansion运行的setlocal /?,看看它是如何启用的,set /?看看它的解释:

@echo off
setlocal enabledelayedexpansion
for %%x in (Veronica, Nils, Mike, Tom) do (
  set "word=%%x"
  set "str=My name is repl"
  call set "str=%%str:repl=!word!%%"
  echo !str!
)

为了使脚本看起来更清晰,您甚至可能希望通过调用变量将名称添加到可以更新的变量中,而无需扩展for循环:

@echo off
set "names=Veronica,Nils,Mike,Tom"
setlocal enabledelayedexpansion
for %%x in (%names%) do (
  set "word=%%x"
  set "str=My name is repl"
  call set "str=%%str:repl=!word!%%"
  echo !str!
)

请注意,我们用%替换!,我们需要扩展变量。也不是我在每个set中的变量名称之前设置我的双引号


0
投票

您的代码中的一些要点:

错误:

  • 使用FOR /L %var IN (start,step,end)语法迭代从startend的数字序列,增加step指定的量。因此循环变量(%var)表示序列中的当前数字。 但是你想迭代一组字符串,为此你必须使用FOR命令的裸变量(没有任何开关的FOR)。文档说它是用于迭代一组文件,但只要字符串不包含通配符*?,它就可以安全地用于迭代字符串集。 因此,作为纠正代码的第一步,必须删除/L开关FOR命令: for %%x in (Veronica, Nils, Mike, Tom) do ...
  • 正常变量扩展;也称为百分比变量扩展,在代码实际执行之前发生,因此在代码块中,您只能可靠地使用百分比扩展来为在进入该代码块之前定义的变量,对同一块中的变量的任何修改百分比扩张(%var%)不会显示,因为它已经扩展到进入区块之前的任何状态。 要克服此限制,您可以将变量周围的百分比加倍,并使用CALLcall echo %%var%%为命令添加前缀或使用延迟变量扩展:echo !var! 所以下一步将是用echo %str%取代call echo %%str%%
  • 变量替换不区分大小写,所以在替换%str:Name=Vernica%中,因为str变量包含两个出现的子串“name”("My name is Name"),它将替换两者,结果将是My Veronica is Veronica。要替换Name部分,您必须使用在主字符串中唯一的子字符串: set "str=My name is $Name" call set str=%%str:$Name=%%x%% 另请注意在替换中直接使用FOR变量%%x。没有必要像使用%%x那样将set word=%%x分配给另一个变量。即使你,它也行不通,因为在call set str=%%str:Name=%word%%%中,变量%word%已经被批处理解析器取代,具有进入FOR块之前的任何值。

总结以上几点,工作代码可以这样写

for %%x in (Veronica, Nils, Mike, Tom) do (
    set "str=My name is $Name"
    call set str=%%str:$Name=%%x%%
    call echo %%str%% >>"names.txt"
)

上述工作正确并产生了所需的结果,但效率非常低,可以进一步优化以产生更高效,更快,更短和更清洁的代码,这将在下一节中描述。

效率低下的问题:

继续上一节中的更正代码

  • str变量是一个静态变量,它在循环的每次迭代中使用相同的字符串My name is $Name初始化它因此它可以移动到循环之外并初始化一次。
  • 由于您只使用替换字符串一次将其写入输出设备,因此可以将setecho组合成单个命令同时执行这两个操作:call echo %%str:$Name=%%x%%
  • 循环中使用的重定向(>>"names.txt"),因此在循环的每次迭代中,文件names.txt将被打开,写入和关闭。这是非常低效的,想想这个数万次的循环。整个FOR循环可以放在一个重定向的块中,因此文件names.txt只在循环启动时打开一次,在循环完成时关闭。
  • 最好将名称项括在引号之间,如果它们包含代码不会受影响的任何空格或特殊字符。实际上,这不是效率低下,而是最佳实践建议。

最后结果

没有延迟扩张:

set "str=My name is $Name"
(for %%x in ("Veronica", "Nils", "Mike", "Tom") do call echo %%str:$Name=%%~x%%)>"names.txt"

延迟扩展:

set "str=My name is $Name"
setlocal EnableDelayedExpansion
(for %%x in ("Veronica", "Nils", "Mike", "Tom") do echo !str:$Name=%%~x!)>"names.txt"
endlocal

比较使用CALLDelayedExpansion

  • CALL非常慢,DelayedExpansion要快得多。
  • 如果名字包含DelayedExpansion!将失败
  • 如果名称中包含任何一个CALL字符,&|<>将失败,除非您在使用echo时将它们用引号括起来。例如call echo "%%str:$Name=%%~x%%"。 当然,这不会发生在人名中,而是通用字符串。
© www.soinside.com 2019 - 2024. All rights reserved.