如何在Windows批处理脚本中从文件中读取带有"!"的字符串?

4
我正在编写一个批处理脚本来读取文件。该文件包含类似token=value的行。我有解析文件每一行的代码,并将其存储在%%i中。以下代码尝试提取token的值:
请注意,此脚本使用延迟扩展,如评论中所述。
   for /f "tokens=1* delims==" %%a in ("%%i") do (  
      if "%%a"=="password" ( set password=%%b )  
   )

如果token密码的值包含"!",那么"!"将被跳过,只有字符串的其余部分存储在变量密码中。例如,如果行是: password=Test!
那么变量password=Test。我尝试了各种方式更改输入文件,但批处理脚本读取的除了"!"之外的所有内容。我使用了以下方法:
1. password="Test!" 2. password="Test^!" 3. password=Test%! password=Test%!
所有这些都跳过了"!"。您有什么办法可以将带有"!"的字符串读入变量中吗?

1
也许 ^^! 可以行得通? - aschipfl
也尝试过了。输入是Test^^!,输出是Test^。 - cppcoder
1
“^^^!”怎么样?(顺便说一句,我是认真的。) - SomethingDark
2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - dbenham
谢谢,@dbenham。当扩展被启用时,我可以验证^^^!在我的答案中与测试脚本一起工作。 - GuitarPicker
2个回答

5
假设您正在使用延迟变量扩展,只需暂时禁用它进行比较即可。为了在非延迟模式下进行比较,我会通过分割 ! 来解析当前行,并检查第一个标记是否与整行相同。
@echo off
setlocal enableDelayedExpansion
for /f "delims=" %%i in (sourcefile.txt) do (
    setlocal disableDelayedExpansion
    for /f "delims=! tokens=1" %%z in ("%%i") do (
        if "%%z"=="%%i" ( rem The line has no !
            for /f "tokens=1* delims==" %%a in ("%%i") do (
                if "%%a"=="password" ( set "password=%%b" )
            )
        )
    )
    endlocal
)
pause

然而,password 变量仅在内部 setlocal 上下文中可用,如果要导出它,请使用此答案


谢谢回复。我确实有“setlocal EnableDelayedExpansion”。如果我只是将其设置为“setlocal disableDelayedExpansion”,那么我就能够使用“!”解析字符串。您知道如果我禁用延迟扩展会有什么影响吗? - cppcoder
@cppcoder,当它被禁用时,循环内的变量(如%password%)将在整个循环被解析和执行之前仅扩展一次,因此在您的情况下它将为空。另请参见启用和禁用延迟扩展,它是做什么的? - wOxxOm
但是在endlocal之后,密码变量将会消失。为了避免这种情况,您需要使用安全返回技术来跨越endlocal屏障 - jeb
没错,这就是我在帖子中谈论的内容。感谢提供链接,已添加到答案中。 - wOxxOm

1
我没有在您发布的代码中遇到那个问题。我认为错误可能出现在您发布的代码之前,该代码将文件的每一行存储在%%i中。
在%%a循环之前放置一个echo语句,以便您可以在进一步处理之前验证%%i的内容。我非常怀疑感叹号根本没有进入%%i。
这是我在Windows 7上运行的完整CMD文件:
@echo off
for /f "tokens=1* delims= " %%i in ("password=Test!") do (

echo i: %%i
   for /f "tokens=1* delims==" %%a in ("%%i") do (  
      if "%%a"=="password" ( set password=%%b )  
   )
)
echo password: %password%

输出如下:
i: password=Test!
password: Test!

所以您可以看到,当%%i包含password=Test!时,Test!完好无损地通过了for循环。如果您想发布批处理文件的早期部分,我们可以帮助您进一步进行故障排除。

我有同样的问题。以下代码用于逐行读取存储在configFile中的文件:for /f "tokens=1* delims= " %%i in (%configFile%) do (用于标记化行的上面的代码)。"%%i"没有"!"。是否有一种方法可以提取行中的"!"? - cppcoder
我的代码加了那行后仍然可以工作。我认为我们遗漏了一些信息。你有使用 setlocal EnableDelayedExpansion 吗?那会改变解析变量中感叹号的行为。你的代码是没问题的,除非我启用了延迟扩展。关闭扩展后它可以运行,但你只能在循环安全退出之后再使用变量 password,而且每个文件只能保留一个密码,因为它只会保留最后一个密码。也许你需要发布你的整个脚本而不是我们目前拥有的片段。 - GuitarPicker
该操作提到他使用了延迟扩展,这就是为什么在您的示例中没有使用延迟扩展也能正常工作的原因。 - jeb
在我之前的评论中,我错过了那个更新。恐怕唯一让它工作的方法是在源文件中转义感叹号,这可能需要在运行此脚本之前对文件进行格式化。也许将其拆分为两个批处理文件会有所帮助,一个被另一个调用。 - GuitarPicker
好的,现在我们已经把所有的事实收集在一起了,一些比这个更好的答案正在浮出水面。太棒了! - GuitarPicker

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