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)));