Why is the FOR /f loop in this batch script evaluating a blank line?

13,853

Solution 1

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

Solution 2

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 (

Solution 3

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.

Solution 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.

Solution 5

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

Share:
13,853
Poo
Author by

Poo

I wished I was a game programmer until I was one. Then I wished I did anything else. Now, I wish I was a game programmer again.

Updated on June 28, 2022

Comments

  • Poo
    Poo almost 2 years

    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.

  • Andriy M
    Andriy M over 12 years
    Wow, we live and learn, don't we? I wonder why would a utility like WMIC produce a single <CR> character on a line in the first place... Or maybe that's the result of what @matt has found out.
  • jeb
    jeb over 12 years
    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
  • Poo
    Poo over 12 years
    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.
  • jeb
    jeb over 11 years
    Nice idea to use for/f for this, normally I found this behaviour annoying, but in this case it's useful.
  • wpg4665
    wpg4665 over 9 years
    This is a much better answer IMO, doesn't require Delayed Expansion, :CALL's, or nested for loops. Easy and simple!