2011-11-04 79 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:\|" 
    )