8

I'm trying to write a batch script that obtains (among other things) a list of all of the disk drives the computer has. The basic code looks something like this:

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:\\|"
)

I pretty obviously build two lists with slightly different formats for use later. When I run this, however, the output I get looks something like this:

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

Now, I expect the trailing pipe in both cases and I can manage that, but I'm really confused why there is an extra blank entry in there. If I run the wmic command manually, I can see that there is indeed a blank line at the end of the output, but my understanding is that /f was specifically supposed to ignore blank lines.

If I turn ECHO on, it looks like that last line is just coming in as a carriage return/newline or similar. Is there a way to do what I'm expecting? Am I missing something? I tried to write an if condition in the loop to exclude this last line, but it was... funky and never worked. I appreciate any/all help.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
Morinar
  • 3,460
  • 8
  • 40
  • 58

7 Answers7

12

I just came over this topic. I've been using findstr /v to exclude empty lines:

FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
dmitry
  • 182
  • 1
  • 9
  • This is a much better answer IMO, doesn't require Delayed Expansion, :CALL's, or nested for loops. Easy and simple! – wpg4665 Jan 14 '15 at 15:22
5

In this case the last iteration produces not an empty item, and you get your output of C|D|E|| only with echo %DISK_DATABASES%,
but echo !DISK_DATABASES! will output ||D|E|??

That's because the last element is a single <CR> character.
And <CR> characters are directly removed after the percent expansion, but not with delayed expansion.

You could avoid this, using the percent expansion to remove them

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
jeb
  • 78,592
  • 17
  • 171
  • 225
  • Wow, we live and learn, don't we? I wonder why would a utility like WMIC produce a single `` character on a line in the first place... Or maybe that's the result of what @matt has found out. – Andriy M Nov 05 '11 at 10:56
  • I suppose it's not the unicode problem, as even in XP Microsoft did't know how their own line endings should be. IPconfig (XP) produces also inconstent line endings – jeb Nov 07 '11 at 11:44
  • Thanks to both jeb and matt for the answers. Both solutions worked, but jeb's didn't require me to create temp files so I'm accepting his. – Morinar Nov 07 '11 at 15:26
4

I discovered a more efficient and more reliable method to strip the unwanted <CR> from the end of each line. No temp file, and no CALL needed.

I don't understand the mechanism of how FOR /F converts the WMIC unicode output into ASCII. Normally FOR /F cannot read unicode. But however it works, each converted line ends with <CR><CR><LF>. FOR /F breaks lines at each <LF>, and then if the last character in the line is <CR> it strips that last <CR>, in this case leaving behind the unwanted <CR>.

The solution is to simply pass each line through one more 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:\\|"
)

This method is more reliable then using normal expansion because you don't have to worry about quoting or escaping special characters. For example, The CALL method that uses normal expansion cannot handle a string like "this & that" & the other. But this method has no problem with such a string.

dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Nice idea to use `for/f` for this, normally I found this behaviour annoying, but in this case it's useful. – jeb Feb 02 '13 at 08:16
4

According to http://ss64.com/nt/for_f.html

Many of the newer commands and utilities (e.g. WMIC) output text files in unicode format, these cannot be read by the FOR command which expects ASCII. To convert the file format use the TYPE command.

So it appears that WMIC and FOR don't play nice together.

matt
  • 476
  • 1
  • 5
  • 11
1

Add ^| findstr . and you will get only not blank lines

    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:\|"
    )
    
0

My standard idiom for dealing with this is to write the output from WMIC to a temp file, then use TYPE (which reduces UTF16 to ASCII) to feed that into FOR, like this:

:: 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
flabdablet
  • 3,565
  • 3
  • 22
  • 15
0

Run the following command:

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

Output will be:

field=value

Note that there will be no extra lines.

slm
  • 15,396
  • 12
  • 109
  • 124
David Lang
  • 81
  • 1
  • 2