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

时间:2018-09-06 03:13:34

标签: windows batch-file cmd

这是我到目前为止写的:

object selectedObject; // Set by mousehandler (this works)
List<MyClass> mcList = new List<MyClass>; // Populated (this works)
if (selectedItem.GetType() == typeof(MyClass)) { CallThisMethod(); }; // (this works)
private void method() 
{
   // MyClass mc = mcList.Find( selectedObject );
}

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

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
)

无限循环。

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

3 个答案:

答案 0 :(得分:0)

Windows命令处理器用从前面的%variable%开始并以匹配的(结束的命令块中{strong>所有出现的)替换为之前引用的环境变量的值使用命令块执行命令。请参见How does the Windows Command Interpreter (CMD.EXE) parse scripts?,可以通过在命令提示符窗口中运行批处理文件来查看由cmd.exe进行的命令块处理,而不用双击删除了@echo off或修改为{{ 1}}或暂时注释掉,直到批处理文件按预期工作为止。

在命令提示符窗口中运行@echo ON时获得的帮助输出在 IF FOR 示例中说明了何时以及如何使用delayed expansion这是在同一命令块中定义或修改的命令块中访问环境变量值的首选方法。

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

在发布的批处理代码中,环境变量str不在命令块的 FOR 命令行上方定义。因此,echo %str% >> names.txt将在完全执行 FOR 之前解析由cmd.exe修改为echo >> names.txt的整个命令块,因此 ECHO 只会输出当前状态的三倍。

此外,由于未在{strong> FOR 命令行之前使用命令块定义call set str=%%str:Name=%word%%%,因此命令行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文章。

答案 1 :(得分: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

的变量名之前设置双引号

答案 2 :(得分: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%替换为{ {1}}。

  • 变量替换不区分大小写,因此在替换call echo %%str%%中,因为%str:Name=Vernica%变量包含两个出现的子字符串“名称”(str),它将同时替换两者,结果将为"My name is Name"。要替换Name部分,您必须使用主字符串中唯一的子字符串:
    My Veronica is Veronica
    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%块之前的值。
    < / p>

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

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 $Nameset可以组合为一个命令,一次执行两个操作:echo

  • 在循环(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 CALL的比较

  • DelayedExpansion非常慢,CALL更快。

  • 如果名称包含DelayedExpansion

  • DelayedExpansion将失败 如果名称中包含!个字符中的任何一个,则

  • CALL将失败,除非使用&|<>时将它们括在引号中。例如echo
    当然,用人名不可能发生这种情况,但是对于通用字符串来说就不会发生。