【问题标题】:DOS batch FOR loop with FIND.exe is stripping out blank lines?带有 FIND.exe 的 DOS 批处理 FOR 循环正在删除空行?
【发布时间】:2026-01-20 06:25:01
【问题描述】:

即使我使用 TYPE.exe 命令转换文件以确保文件是 ASCII 以便 FIND 命令兼容,此 DOS 批处理脚本正在删除空行并且不显示文件中的空行与文件。谁能告诉我如何让这个脚本包含空行?

@ECHO off
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE.exe "build.properties" ^| FIND.exe /V ""`) DO (
  ECHO --%%A--
)
pause

【问题讨论】:

    标签: batch-file dos cmd


    【解决方案1】:

    这是 FOR /F 的设计行为 - 它从不返回空行。解决方法是使用 FIND 或 FINDSTR 在行前加上行号。如果您可以保证没有行以行号分隔符开头,那么您只需设置适当的分隔符并保留标记 1* 但只使用第二个标记。

    ::preserve blank lines using FIND, assume no line starts with ]
    ::long lines are truncated
    for /f "tokens=1* delims=]" %%A in ('type "file.txt" ^| find /n /v ""') do echo %%B
    
    ::preserve blank lines using FINDSTR, assume no line starts with :
    ::long lines > 8191 bytes are lost
    for /f "tokens=1* delims=:" %%A in ('type "file.txt" ^| findstr /n "^"') do echo %%B
    
    ::FINDSTR variant that preserves long lines
    type "file.txt" > "file.txt.tmp"
    for /f "tokens=1* delims=:" %%A in ('findstr /n "^" "file.txt.tmp"') do echo %%B
    del "file.txt.tmp"
    

    我更喜欢 FINDSTR - 它更可靠。例如,FIND 可以截断长行 - FINDSTR 不会,只要它直接从文件中读取。通过管道或重定向从标准输入读取时,FINDSTR 确实会删除长行。

    如果文件可能包含以分隔符开头的行,那么您需要保留带有行号前缀的整行,然后使用搜索和替换来删除行前缀。将 %%A 传输到环境变量时,您可能希望延迟扩展关闭,否则任何!将被破坏。但稍后在循环中,您需要延迟扩展来进行搜索和替换。

    ::preserve blank lines using FIND, even if a line may start with ]
    ::long lines are truncated
    for /f "delims=" %%A in ('type "file.txt" ^| find /n /v ""') do (
      set "ln=%%A"
      setlocal enableDelayedExpansion
      set "ln=!ln:*]=!"
      echo(!ln!
      endlocal
    )
    
    ::preserve blank lines using FINDSTR, even if a line may start with :
    ::long lines >8191 bytes are truncated
    for /f "delims=*" %%A in ('type "file.txt" ^| findstr /n "^"') do (
      set "ln=%%A"
      setlocal enableDelayedExpansion
      set "ln=!ln:*:=!"
      echo(!ln!
      endlocal
    )
    
    ::FINDSTR variant that preserves long lines
    type "file.txt" >"file.txt.tmp"
    for /f "delims=*" %%A in ('findstr /n "^" "file.txt.tmp"') do (
      set "ln=%%A"
      setlocal enableDelayedExpansion
      set "ln=!ln:*:=!"
      echo(!ln!
      endlocal
    )
    del "file.txt.tmp"
    

    如果您不需要担心将文件转换为 ASCII,那么删除管道并让 FIND 或 FINDSTR 打开指定为参数的文件或通过重定向会更有效。

    还有另一种解决方法可以在读取过程中完全绕过 FOR /F。它看起来很奇怪,但它更有效。使用延迟扩展没有任何限制,但不幸的是它还有其他限制。

    1) 行必须由 终止(如果你进行 TYPE 文件转换,这不会有问题)

    2) 行的长度必须

    3) 任何尾随控制字符都会从每一行中去除。

    4) 它必须从文件中读取——你不能使用管道。因此,在您的情况下,您将需要使用临时文件来进行 ASCII 转换。

    setlocal enableDelayedExpansion
    type "file.txt">"file.txt.tmp"
    for /f %%N in ('find /c /v "" ^<"file.txt.tmp"') do set cnt=%%N
    <"file.txt.tmp" (
      for /l %%N in (1 1 %cnt%) do(
        set "ln="
        set /p "ln="
        echo(!ln!
      )
    )
    del "file.txt.tmp"
    

    【讨论】:

    【解决方案2】:

    我编写了一个非常简单的程序,当FINDFINDSTR 命令用于此目的时,它可以替代它们。我的程序叫做PIPE.COM,它只是在空行中插入一个空格,所以所有的行都可以直接由FOR命令处理,无需进一步调整(只要插入的空格不在乎)。这里是:

    @ECHO off
    if not exist pipe.com call :DefinePipe
    FOR /F "USEBACKQ delims=" %%A IN (`pipe ^< "build.properties"`) DO (
      ECHO(--%%A--
    )
    pause
    goto :EOF
    
    :DefinePipe
    setlocal DisableDelayedExpansion
    set pipe=´)€ì!Í!ŠÐŠà€Ä!€ü.t2€ü+u!:æu8²A€ê!´#€ì!Í!².€ê!´#€ì!Í!²+€ê!´#€ì!Í!Šò€Æ!´,€ì!Í!"Àu°´LÍ!ëÒ
    setlocal EnableDelayedExpansion
    echo !pipe!>pipe.com
    exit /B
    

    编辑附录作为新评论的答案

    :DefinePipe 子例程中的代码创建了一个名为 pipe.com 的 88 字节程序,该程序基本上执行了与此伪批处理代码等效的过程:

    set "space= "
    set line=
    :nextChar
       rem Read just ONE character
       set /PC char=
       if %char% neq %NewLine% (
          rem Join new char to current line
          set line=%line%%char%
       ) else (
          rem End of line detected
          if defined line (
             rem Show current line
             echo %line%
             set line=
          ) else (
             rem Empty line: change it by one space
             echo %space%
          )
       )
    goto nextChar
    

    这样,输入文件中的空行被替换为一个空格的行,因此 FOR /F 命令不再省略它们。正如我在回答中所说,这“只要插入的空间不关心”就可以工作。

    请注意,pipe.com 程序不适用于 64 位 Windows 版本。

    安东尼奥

    【讨论】:

    • 感谢 dos 提示的帮助
    • 对不起。您能否确认我的 PIPE.COM 程序是否解决了您的问题?
    • @Aacini - 你能更详细地描述一下这个解决方案的工作原理吗?
    • @Aacini - 谢谢。虽然我并没有完全遵循它(我相信我现在意识到%pipe% 包含可执行程序的编译代码)。如果你不介意,你能解释一下:C 在set /PC char= 中做了什么以及¿这条线只需要一个字符吗?其中定义了%NewLine% 的内容; echo 不会将字符/空格和尾随换行符加入%line%;最后,什么变量是所有单个字符在每一 ¿ 秒内一个接一个地连接起来的?迭代。还有,64位的版本能不能不能造?
    • @user66001:正如我在回答中所说,这是伪批处理代码,不是真实代码。这只是向您解释 pipe.com 做什么的一种方式,而不是:“读取下一个字符,如果不是 NL 附加到当前行,否则显示当前行,但如果它是空的,则插入一个空格”。由于您提到的细节,这个过程不能通过批处理文件来实现。最初的 pipe.asm 程序是用汇编语言编写的。
    【解决方案3】:

    输出行包括空行

    这是我为自己开发的一种方法。

    将代码保存为批处理文件,例如 SHOWALL.BAT 并将 源文件 作为命令行参数传递。

    输出可以重定向或管道。

    @echo off
    
    for /f "tokens=1,* delims=]" %%a in ('find /n /v "" ^< "%~1"') do echo.%%ba
    
    exit /b
    

    示例:

    showall source.txt

    showall source.txt >destination.txt

    showall source.txt | 查找“字符串”

    一个奇怪的地方是包含 '^'(重定向),而不是仅仅执行以下操作:

    for /f "tokens=1,* delims=]" %%a in ('find /n /v "" "%~1"') do echo.%%ba
    

    通过省略重定向,输出前导空行。

    【讨论】:

    • 不错,但它会从行中删除前导 ] 字符,如果存在像 \..\..\Windows\system32\calc.exe 这样的行,echo. 将失败
    • @jeb ,实际上,至少在 Windows 7 上,它不会删除所有 ] 字符。只是第一个,或者直到它遇到*
    【解决方案4】:

    感谢 dbenham,虽然它与他的建议略有不同:

    ::preserve blank lines using FIND, no limitations
    for /f "USEBACKQ delims=" %%A in (`type "file.properties" ^| find /V /N ""`) do (
      set "ln=%%A"
      setlocal enableDelayedExpansion
      set "ln=!ln:*]=!"
      echo(!ln!
      endlocal
    )
    

    【讨论】:

    • 什么不起作用?你在看什么?我看到一个问题。如果该行为空白,ECHO !ln! 将打印ECHO is off!这就是为什么我使用ECHO(!ln!
    • 好的,我更新了。现在可以了。非常感谢!现在我只需要将它集成到我的项目中。
    【解决方案5】:

    正如this 对上述问题的回答中所述,默认情况下,在(至少)Windows XP 中使用for /f 似乎不会跳过行(社区 - 请通过测试以下批次来更新此答案Windows 的版本和服务包上的命令)。


    编辑:根据Jebcomment below,似乎ping 命令至少在Windows XP 中是
    导致for /f 产生&lt;CR&gt; 而不是空行(如果有人知道具体原因,会
    如果他们能更新这个答案或评论,我们将不胜感激)。

       作为一种解决方法,似乎第二个默认分隔标记(示例中为&lt;space&gt; / %%b
    返回为blank,这适用于我通过“父级”消除空白行的情况
    iffor /f 开头的第二个标记为条件,如下所示:

       for /f "tokens=1,2*" %%a in ('ping -n 1 google.com') do (
          if not "x%%b"=="x" (
             {do things with non-blank lines}
          )
       )
    



    使用以下代码:

    @echo off
    systeminfo | findstr /b /c:"OS Name" /c:"OS Version" 
    echo.&echo.
    ping -n 1 google.com
    echo.&echo.
    for /f %%a in ('ping -n 1 google.com') do ( echo "%%a" )
    echo.&echo.&echo --------------&echo.&echo.
    find /?
    echo.&echo.
    for /f %%a in ('find /?') do ( echo "%%a" )
    echo.&echo.
    pause
    

    ....以下是我在 Windows XP、Windows 7 和 Windows 2008 上看到的,这是我可以访问的仅有的三个 Windows 版本和服务包:

    【讨论】:

    • 就像 dbenham 说的:对于 /f 条总是空行。在您的情况下,XP 似乎不会删除空白行,但实际上您会得到一条在"Pinging""Reply" 之间只有一个单引号的行。但这并不意味着 FOR/F 获取一个空行。该行包含一个&lt;CR&gt; 字符,因此光标跳回到行首,第一个引号被最后一个引号覆盖,仅此而已。 XP下的ping命令简直就是bug
    • 谢谢@jeb。我来到这个页面是因为(我当时认为是)额外空格的问题,但现在意识到(由你更正)它们只是在 XP 上输出的回车。您能否也建议如何在if not "x%%a"="" 语句中测试&lt;cr&gt;
    • 要删除CR,您可以调用:trimmer &lt;LF&gt; set "trim=%1" 这样的函数,因为所有CR 在百分比扩展阶段之后都将被删除。但是对于更多问题,您应该打开一个自己的问题
    • 您没有正确使用“~”字符。它会为您删除字符串外部的引号。了解这一点可能是您问题的根源。而不是 '%%a' 你应该使用 '%%~a' 。
    • @djangofan 一开始不明白,我根本没有使用~,更不用说正确了。但是,按照this screenshot 所示插入它后,我一定做错了。问题是,正如我相信 jeb 在上面正确识别的那样,for /f 并没有完全忽略空行,我猜它正在删除 lf,而不是 Win XP 上的 cr。我只是把它用引号括起来,看看它输出了什么。