2011-11-04 43 views
8

我想写一个批处理脚本,获取(除其他外)计算机所有磁盘驱动器的列表。基本代码看起来是这样的:为什么此批处理脚本中的FOR/f循环评估空行?

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条件来排除最后一行,但它是......时髦而且从来没有工作过。我感谢任何/所有的帮助。

回答

5

在这种情况下,最后一次迭代产生不是空洞的项目,你会得到你的C|D|E||只有echo %DISK_DATABASES%输出,
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 
+0

哇,我们的生活和学习,不是吗?我想知道为什么像WMIC这样的实用程序首先会在一行中生成一个''字符......或者这可能是@matt发现的结果。 –

+0

我想这不是unicode问题,因为即使在XP中,微软也不知道自己的行结尾应该如何。 IPconfig(XP)也产生不一致的结局 – jeb

+0

感谢jeb和matt的答案。两种解决方案都可以工作,但jeb's不需要我创建临时文件,所以我接受他的。 – Morinar

4

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

许多较新的命令和实用Unicode格式(例如WMIC)输出的文本文件,这些不能由一个希望ASCII FOR命令读出。 要使用TYPE命令转换文件格式。

因此,看起来WMIC和FOR在一起玩得不好。

3

我发现了一种更有效和更可靠的方法从每一行的端部条不需要的<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这样的字符串。但是这种方法对于这样的字符串没有问题。

+0

为此使用'for/f'的好主意,通常我发现这种行为很烦人,但在这种情况下它很有用。 – jeb

0

我处理这一标准成语是从WMIC输出写入到一个临时文件,然后使用类型(减少UTF-16到ASCII)喂那成,像这样:

:: 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 
0

运行下面的命令:

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

输出将是:

field=value 

注意,将有没有多余的线。

11

我刚刚来过这个话题。我一直在使用FINDSTR/V排除空行:

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

这是IMO更好的答案,不需要延迟扩展,:CALL或嵌套for循环。简单而简单! – wpg4665

+0

确实好多了,thx德米特里 – Deus777

0

添加^| findstr .,您将获得不仅没有空行

 
    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" ^| findstr .') do (
     SET "DISK_DATABASES=!DISK_DATABASES!%%a|" 
     SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\|" 
    )