首页 > 解决方案 > 转义“!” 在具有延迟变量扩展的 for 循环中

问题描述

我需要逃脱“!” (和其他特殊字符)在启用延迟变量扩展的 for 循环中

我试过用字符串替换手动转义循环变量^^!在循环变量中, %%a 但根本没有运气。在 for 阅读它们之后是否已经太晚了?如果是这样,我怎么能做到这一点?

下面是一个简短的函数。这里唯一相关的部分是 for 循环和 echo 语句。那就是每 X 行从文件中打印出整行,这些行是文件路径。它们(有时)包含诸如“!”之类的字符 和其他麻烦的特殊字符。我只想在这里 echo 传递它而不解释它 - 但它最终会删除我的“!” 字符。对于我的使用,它们需要完全正确,否则它们是无用的,因为它们必须与我稍后使用它们的实际文件相关联。

setlocal EnableDelayedExpansion

:SplitList
if [%3] == [] goto :SplitListUsage
set inputfile=%~1
set outputfile=%~2
set splitnumber=%~3
set skipLines=0
set skipLines=%~4

if %skipLines% GTR 0 (
  set skip=skip=%skipLines%
) else (
  set skip=
)

@echo off > %outputfile%

set lineNumber=0
for /f "tokens=* %skip% delims= " %%a in (%inputfile%) do (
    set /a modulo="!lineNumber! %% !splitnumber!"
    if "!modulo!" equ "0" (
        echo %%a >> !outputfile!
    )
    set /a lineNumber+=1
)
exit /B 0

标签: batch-file

解决方案


快速解决方案:

 if !modulo! equ 0 (
     setlocal DisableDelayedExpansion
     (echo %%a)>> "%outputfile%"
     endlocal
 )


更好的解决方案:

根据您的代码示例,无需为整个代码启用延迟扩展,
实际上您应该将其禁用,以免与可能包含的文件名或输入字符串混淆!,并在必要时启用它:

setlocal DisableDelayedExpansion

REM Rest of the code...

for /f "tokens=* %skip% delims= " %%a in (%inputfile%) do (
    set /a "modulo=lineNumber %% splitnumber"
    setlocal EnableDelayedExpansion
    for %%m in (!modulo!) do (
        endlocal
        REM %%m is now have the value of modulo
        if %%m equ 0 (
            (echo %%a)>> "%outputfile%"
        )
    )
    set /a lineNumber+=1
)


旁注:

您的代码还有一些其他问题,您可能已经注意到,其中一些问题已在上述解决方案中得到纠正。但为了不分散您对主要问题的注意力,我在这里分别介绍了它们。

写入outputfile.

这是覆盖其余部分的重写代码:

@echo off
setlocal DisableDelayedExpansion

:SplitList
if "%~3"=="" goto :SplitListUsage
set "inputfile=%~1"
set "outputfile=%~2"
set "splitnumber=%~3"
set "skipLines=0"
set /a "skipLines=%~4 + 0" 2>nul

if %skipLines% GTR 0 (
  set "skip=skip=%skipLines%"
) else (
  set "skip="
)

set "lineNumber=0"
(
    for /f "usebackq tokens=* %skip% delims= " %%a in ("%inputfile%") do (
        set /a "modulo=lineNumber %% splitnumber"
        setlocal EnableDelayedExpansion
        for %%m in (!modulo!) do (
            endlocal
            REM %%m is now have the value of modulo
            if %%m equ 0 echo(%%a
        )
        set /a lineNumber+=1
    )
)>"%outputfile%"
  • 您没有使用双引号来保护变量赋值,"例如set inputfile=%~1. 如果现在裸批处理参数%~1包含空格或特殊字符,例如&您的批处理文件失败,则可能因语法错误而致命,或者在执行时数据不正确。推荐的语法是使用set "var=value"不将引号分配给变量值但提供对特殊字符的保护。它还保护分配免受意外尾随空格的影响。
  • 可能包含特殊字符或空格,因此在's IN 子句%inputfile%中使用时应通过双引号对其进行保护。双引号时,参数FOR /F中的文件名也必须使用。FOR /Fusebackq
  • SET /A无需扩展变量值set /a modulo="!lineNumber! %% !splitnumber!":. 变量名可以直接使用,不管有没有延迟扩展,它都能正常工作。
  • (echo %%a) >> "%outputfile%"在循环内使用FOR会导致严重的性能损失,特别是在大量迭代时,因为在每次迭代中,输出文件都会被打开、写入然后关闭。提高性能整个FOR循环可以重定向一次。任何新数据都将附加到已打开的文件中。
  • 奇怪的外观echo(是为了防止空变量值,或者当变量值为/?. 如果变量为空,使用echo %%a可能会打印“ECHO 开启/关闭”消息,或者可能会打印回显使用帮助。
  • 在主要解决方案中,我使用(echo %%a)>> "%outputfile%"代替的原因echo %%a >> "%outputfile%"是为了防止输出 and 之间的额外%%a空间>>。现在您知道了使用的原因echo(,很容易理解更安全的替代方案:(echo(%%a)>> "%outputfile%"

推荐阅读