【问题标题】:Why is the FOR /f loop in this batch script evaluating a blank line?为什么此批处理脚本中的 FOR /f 循环评估一个空行?
【发布时间】:2011-12-22 08:43:22
【问题描述】:

我正在尝试编写一个批处理脚本来获取(除其他外)计算机拥有的所有磁盘驱动器的列表。基本代码如下所示:

REM Build the list of disk drives to monitor
SETLOCAL enabledelayedexpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
    SET "DISK_DATABASES=!DISK_DATABASES!%%a|"
    SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\\|"
)

很明显,我构建了两个格式略有不同的列表以供以后使用。但是,当我运行它时,我得到的输出如下所示:

C|D|E||
C:\\|D:\\|E:\\|:\\|

现在,我希望在这两种情况下都有尾随管道,我可以做到这一点,但我真的很困惑为什么那里有一个额外的空白条目。如果我手动运行wmic 命令,我可以看到输出的末尾确实有一个空行,但我的理解是/f 是专门应该忽略空行的。

如果我打开ECHO,看起来最后一行只是作为回车/换行或类似的形式出现。有没有办法做我所期待的?我错过了什么吗?我试图在循环中写一个if 条件来排除最后一行,但它是......时髦而且从来没有工作过。感谢任何/所有帮助。

【问题讨论】:

    标签: windows batch-file cmd wmic


    【解决方案1】:

    添加^| findstr . 你只会得到不是空行

    REM 建立要监控的磁盘驱动器列表 SETLOCAL 启用延迟扩展 FOR /f "skip=1 tokens=1 delims=:" %%a in ( '"WMIC logicaldisk WHERE drivetype=3 GET deviceid" ^| findstr .') 做 ( SET "DISK_DATABASES=!DISK_DATABASES!%%a|" SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\|" )

    【讨论】:

      【解决方案2】:

      我刚想到这个话题。我一直在使用 findstr /v 来排除空行:

      FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
      

      【讨论】:

      • 这是一个更好的答案 IMO,不需要延迟扩展、:CALL 或嵌套 for 循环。简单易行!
      【解决方案3】:

      运行以下命令:

      wmic blah /value | find "=" >> wherever
      

      输出将是:

      field=value
      

      请注意,不会有多余的行。

      【讨论】:

        【解决方案4】:

        我处理此问题的标准习惯用法是将 WMIC 的输出写入临时文件,然后使用 TYPE(将 UTF16 简化为 ASCII)将其输入 FOR,如下所示:

        :: Standard environment setup
        setlocal enabledelayedexpansion
        :: Every variable whose name starts with "tf" will identify a temporary
        :: file - remove any such variables inherited from the parent environment
        for /f %%V in ('set tf') do set %%V=
        :: Create some temporary filenames. Prefix all of them with this script's
        :: own name to avoid clashes with those owned by other scripts.
        for /l %%I in (1,1,4) set tf%%I="%temp%\%~n0-temp%%I.txt"
        
        :: Use temp file to work around coding mismatch between WMIC out and FOR in
        wmic product where "name like 'Microsoft Office %% 2010'" get packagecache >!tf1!
        for /f "skip=1" %%P in ('type !tf1!') do if exist "%%~P" msiexec /x "%%~P" /passive /norestart
        
        :: Before quitting script, clean up temporary files
        for /f %%V in ('set tf') do if exist "%%~V" del /f /q "%%~V"
        endlocal
        

        【讨论】:

          【解决方案5】:

          我发现了一种更有效、更可靠的方法来去除每行末尾不需要的<CR>。没有临时文件,也不需要 CALL。

          我不明白 FOR /F 如何将 WMIC unicode 输出转换为 ASCII 的机制。通常 FOR /F 无法读取 unicode。但不管它如何工作,每个转换后的行都以<CR><CR><LF> 结尾。 FOR /F 在每个<LF> 处换行,然后如果行中的最后一个字符是<CR>,它会删除最后一个<CR>,在这种情况下留下不需要的<CR>

          解决方案是简单地将每一行通过一个 FOR /F :-)

          @echo off
          setlocal enableDelayedExpansion
          for /f "skip=1 delims=" %%A in (
            'wmic logicaldisk where "drivetype=3" get deviceid'
          ) do for /f "tokens=1 delims=:" %%B in ("%%A") do (
            set "disk_databases=!disk_databases!%%B|"
            set "drives_to_monitor=!drives_to_monitor!%%B:\\|"
          )
          

          这种方法比使用普通扩展更可靠,因为您不必担心引用或转义特殊字符。例如,使用普通扩展的 CALL 方法不能处理像"this & that" & the other 这样的字符串。但是这种方法对这样的字符串是没有问题的。

          【讨论】:

          • 使用for/f 的好主意,通常我觉得这种行为很烦人,但在这种情况下它很有用。
          【解决方案6】:

          在这种情况下,最后一次迭代产生的不是空项目,您只能使用echo %DISK_DATABASES% 获得C|D|E|| 的输出,
          但是echo !DISK_DATABASES! 会输出||D|E|??

          这是因为最后一个元素是单个 <CR> 字符。
          并且<CR> 字符在百分比扩展后直接删除,而不是延迟扩展。

          你可以避免这种情况,使用百分比扩展来删除它们

          setlocal EnableDelayedExpansion
          FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
            set "item=%%a"
            call :removeCR
          
            if not "!item!"=="" (
              SET "DISK_DATABASES=!DISK_DATABASES!!item!|"
              SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!!item!:\\|"
            )
          )
          goto :eof
          :removeCR
          
          :removeCR
          set "Item=%Item%"
          exit /b
          

          【讨论】:

          • 哇,我们生活和学习,不是吗?我想知道为什么像 WMIC 这样的实用程序首先会在一行上生成一个 <CR> 字符......或者这可能是 @matt 发现的结果。
          • 我想这不是 unicode 问题,因为即使在 XP 中微软也不知道他们自己的行尾应该是怎样的。 IPconfig (XP) 也会产生不一致的行尾
          • 感谢 jeb 和 matt 的回答。两种解决方案都有效,但 jeb 不需要我创建临时文件,所以我接受了他的。
          【解决方案7】:

          根据http://ss64.com/nt/for_f.html

          许多较新的命令和实用程序(例如 WMIC)以 unicode 格式输出文本文件,这些不能被需要 ASCII 的 FOR 命令读取。 要转换文件格式,请使用 TYPE 命令。

          所以看起来 WMIC 和 FOR 不能很好地结合在一起。

          【讨论】:

            猜你喜欢
            • 2023-04-05
            • 2016-03-19
            • 1970-01-01
            • 2014-09-03
            • 2017-02-03
            • 2016-05-17
            • 1970-01-01
            • 2023-03-06
            • 2017-10-05
            相关资源
            最近更新 更多