0

this is a part of a batch that uses wmic.exe to print a text containing different system information. in one part where i use calls and labels to make automated parameters for hard disk information i had to use parameters within parameters to get it to work. this only works with delayed expansions enabled. so in the for /f loop containing the code that echoes to file the lines that are being echoed and has a delayed expansion in them echoes a newline character directly after the end of the expansion! i don't know why or how to go around this. and i have set my mind on doing this with batch and yes i know this A LOT easier to do with wmi in a vbs script. any help is much appreaciated

@echo off

for /f "skip=2 delims=" %%a in ('wmic diskdrive get medialoaded') do call :getdisk

for /f "skip=2 delims=" %%a in ('wmic diskdrive get medialoaded') do call :setdisk

>%tmp%\t.x echo wscript.echo round^(wscript.arguments^(0^) / ^(1024 * 1024 * 1024^), 1^)

for /f "skip=2 delims=" %%a in ('wmic diskdrive get medialoaded') do call :numdisk

goto:eof

:getdisk
set /a disk=%disk%+1
goto:eof

:setdisk
if not defined numreset set /a numreset=%disk%-1
if not defined num (set num=0) else (set /a num=%num%+1)
for /f "tokens=1-10 delims==" %%a in ('wmic diskdrive %num% get interfacetype^,model^,size /format:list') do set %%a%num%=%%b
if "%num%"=="%numreset%" set "num="
goto:eof

:numdisk
if not defined num (set num=0) else (set /a num=%num%+1)
set /a hdd=%num%+1
setlocal enabledelayedexpansion
set interfacetype=interfacetype%num%
set mediatype=mediatype%num%
set size=size%num%
if "%disk%" geq "%num%" >>hdd.txt echo HDD %hdd%:      index %num%
if "%disk%" geq "%num%" >>hdd.txt echo InterFace:  !%interfacetype%!
if "%disk%" geq "%num%" >>hdd.txt echo MediaType:  !%mediatype%!
if "%disk%" geq "%num%" for /f %%a in ('cscript //nologo //e:vbscript %tmp%\t.x "!%size%!"') do set sizegb=%%a
if "%disk%" geq "%num%" >>hdd.txt echo Size:       !%size%! b ^(%sizegb% gb^)
if "%disk%" geq "%num%" >>hdd.txt echo.
endlocal
goto:eof

workaround:

since the linefeed was inside the expanded variable it was simple to remove it:

::before:
!%var%!

::after:
!%var%:~0,-1!
TRSyntax
  • 23
  • 1
  • 9
  • this might help: http://stackoverflow.com/a/23067935/2152082 – Stephan Apr 17 '14 at 15:52
  • it helped me realize something about the wmic command i wasn't aware off. i'll use other places. thanks. but the problem in my question is caused by something else. when the numbered parameteres are first set under the ':setdisk' label, there is no additional linefeed or other data from wmic set to them. the linefeed is appended during the expansion of the !%variable%! inside the echo to file. – TRSyntax Apr 17 '14 at 17:35
  • correction: the newline is indeed INSIDE the expanded variable. you might be on to something after all! but when i echo outside the local memory with delayed expansions enabled as %variable% the new line is not registred by the echo.. – TRSyntax Apr 17 '14 at 18:00

1 Answers1

3

OMG! That code is horrifically and needlessly complex and inefficient - I can barely follow the logic. More power to you for getting it to work, but I think I can show you a better way :)

First off, it is not an extra newline or linefeed (<LF>, hex 0x0A, decmal 10), it is an extra carriage return (<CR>, hex 0x0D, decimal 13). The unwanted character is an odd artifact of how FOR /F interacts with the unicode output of WMIC. FOR /F attempts to convert the unicode into ANSI, but somehow terminates each line of output with <CR><CR><LF>. FOR /F ends each line before the <LF>, then strips the last character if it happens to be <CR>, which leaves one unwanted trailing <CR> at the end of every line.

One foolproof way to eliminate the unwanted character is to use an extra FOR /F, since it will strip the last character if it is <CR>:

for /f "delims=" %%A in ('WMIC ...') do for /f "yourOptions" %%B in ("%%A") do ...

The nice thing about this method is it does not require delayed expansion.

But since you are already using delayed expansion, your method of setting a variable and then using a substring operation to trim off the last character works perfectly fine.


Now onto developing a more sensible code base to get your desired result:

There is no need to parse the WMIC output multiple times - you can do everything within a single FOR /F loop that executes WMIC only once.

Instead of LIST format, you can use CSV. This puts all of the needed values for a disk drive on one line, comma delimited. The only drawback is there could be empty values, which would cause problems with FOR /F parsing, Consecutive delimiters are treated as one, so tokens could be missed. The trick is to use search and replace to put quotes around each token. A missing value then becomes ,"",, which can easily be parsed by a second FOR /F. Use %%~A to later strip the quotes from each token.

Your WMIC command gets Model, which you don't use, and your output references Media Type, which you did not get. I fixed the code to get and output both.

You run a different WMIC for each disk index, but the WMIC DISKDRIVE provides an INDEX value that you can get, so why not use it. My output is not sorted in index order, but all the info is there. If really needed, additional code could be added to sort the results by index.

It is more efficient to enclose your outer FOR /F within parentheses and then redirect that output just once.

Finally, there is no need for a temporary VBS script to do your math. It is easy to embed and call JScript within the batch script instead.

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScrpt comment

::************ Batch portion ***********
@echo off
setlocal enableDelayedExpansion
set cnt=0
(
  for /f "skip=2 delims=" %%A in (
    '"wmic diskdrive get interfacetype,model,size,index,mediaType /format:csv"'
  ) do (
    set "ln=%%A"
    set "ln=!ln:~0,-1!"
    set "ln="!ln:,=","!""
    set /a cnt+=1
    for /f "tokens=2-6 delims=," %%B in ("!ln!") do (
      echo HDD !cnt!: Index %%~B
      echo Interface: %%~C
      echo Model: %%~E
      echo Media: %%~D
      set "size="
      if %%F neq "" for /f %%N in (
        'cscript //E:JScript //nologo "%~f0" "Math.round(%%~F/1024/1024/1024*10)/10"'
      ) do set "size=%%N GiB"
      echo  Size: !size!
      echo(
    )
  )
)>hdd.txt
exit /b

************ JScript portion ***********/
WScript.StdOut.WriteLine(eval(WScript.Arguments.Unnamed(0)));

EDIT: I just realized that a Model description might include commas, which would cause problems with FOR /F parsing CSV format. So here is another solution that reverts back to List format. It turns out the code is even simpler :)

I use the second FOR /F trick to remove the unwanted trailing <CR>.

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScrpt comment

::************ Batch portion ***********
@echo off
setlocal enableDelayedExpansion
set cnt=0
(
  for /f "delims=" %%L in (
    '"wmic diskdrive get interfaceType,model,size,index,mediaType /format:list"'
  ) do for /f "tokens=1* delims==" %%A in ("%%L") do (
    if %%A equ Index (
      if !cnt! gtr 0 echo(
      set /a cnt+=1
      echo HDD !cnt!: Index %%B
    ) else if %%A equ Size (
      set "size="
      if "%%B" neq "" for /f %%N in (
        'cscript //E:JScript //nologo "%~f0" "Math.round(%%B/1024/1024/1024*10)/10"'
      ) do set "size=%%N GiB"
      echo %%A: !size!
    ) else echo %%A: %%B
  )
)>hdd.txt
exit /b

************ JScript portion ***********/
WScript.StdOut.WriteLine(eval(WScript.Arguments.Unnamed(0)));
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • aah, the master has arrived :) yes, i'll admit my methods are.. unorthodox hehe. and belive me i have gone around this tenfolds of different ways. the batch containing the above code uses wmic for alot of other stuff as well and at one point i had to use a vbs script to get the processor socket since i can't seem to find it in wmic. but the hybrid solution is much better in the long run! i'll implement it. as for using the csv format with wmic.. i really have tried it but always get "Invalid XSL format (or) file name." when trying. the same error occurs in your above sample! any ideas? – TRSyntax Apr 18 '14 at 07:15
  • i'll just add that i have tried: http://stackoverflow.com/questions/9673057/wmic-error-invalid-xsl-format-in-windows7 – TRSyntax Apr 18 '14 at 07:34
  • oops, i missed some parens and now it works :) thanks for you solution dave. i'll mark this as the answer as it solves all my issues – TRSyntax Apr 18 '14 at 07:49
  • @Profess0r1011000 - I just realized commas within a Model description would cause problems parsing CSV format. I added a second solution that reverts back to LIST format. – dbenham Apr 18 '14 at 10:23
  • just when i was ten lines from re-writing the whole batch :P well i guess this only applies where there might actually be commas in the values. so i guess i'll go over it again and make some changes for values where i am uncertain. thanks for adding this to your answer! – TRSyntax Apr 18 '14 at 11:08
  • been fiddeling around with your for in a for example for a while now and i'm getting to see the power of it :) it helped me replace 6 sub labels i really didn't want. i can propably remove them all! just have to say thanks again! – TRSyntax Apr 18 '14 at 12:20