0

I was looking for an easy way to get the system volumes info in GB (batch) so I got this:

for /f "tokens=1-3" %a in ('WMIC LOGICALDISK GET FreeSpace^,Name^,Size ^|FINDSTR /I /V "Name"') do @echo wsh.echo "%b" ^& " free=" ^& FormatNumber^(cdbl^(%a^)/1024/1024/1024, 2^)^& " GiB"^& " size=" ^& FormatNumber^(cdbl^(%c^)/1024/1024/1024, 2^)^& " GiB" > %temp%\tmp.vbs & @if not "%c"=="" @echo(& @cscript //nologo %temp%\tmp.vbs >> c:\test\test2.txt | type C:\test\test2.txt

It works fine if I just copy and paste it to the CMD but if I run it from a .bat file it just don't do anything.

What could I be doing wrong?

PD: I can't use PowerShell, I need it in batch.

4
  • That's not batch, it's a fruitless attempt to write vbs in a single line pass values to it wrap that in a for /f processing wmic output. wmic can return proper values but batch is limited to signed 32bit calculations what's +/-2GB. Why can't you use PowerShell? There were 2 similar questions the last two days. Use the search box above. Commented Jun 28, 2018 at 19:59
  • 1
    Batch files require the %-signs for all for meta-variables to be doubled, like %%a, for instance... By the way, I don't understand the | type C:\test\test2.txt; shouldn't it actually read & type C:\test\test2.txt? Commented Jun 28, 2018 at 20:11
  • I can't use powershell because I need to run this on different machines and some of them have powershell disabled. @aschipfl I'll add the missing %, If I use "&" symbol it actually displays the output two times but if I use " | " it displays the output 1 time as it should be. Not sure if "| " is not a valid symbol, I'm a linux guy and this is my first time dealing with windows scripts. Commented Jun 28, 2018 at 20:27
  • 1
    Related: Get size of a directory in 'MB' using batch file. By the way, you should not query the Name but the DeviceID value; see also this SU post... Commented Jun 28, 2018 at 20:34

1 Answer 1

1

There are the following issues in your code:

  • in batch files, for meta-variables must be preceded by two %-signs in contrast to one in command prompt (cmd), hence change %a, etc., to %%a, etc.; otherwise, a syntax error arises;
  • you could avoid filtering out the headline of the output of wmic by findstr /V when using the skip=1 option of for /F;
  • instead of the property Name I recommend to use DeviceID, according to this Super User thread: What is the difference between properties Name, Caption and DeviceID (when executing wmic LogicalDisk)?;
  • not sure what drives you want to query, but perhaps you want local disks only; if so, add the clause where "DriveType=3"; refer to this Microsoft article: Win32_LogicalDisk class;
  • creation of the temporary VBScript script should happen inside of the if condition where it is called as well in order to avoid pointless file write activities;
  • you are appending to the text file in every loop iteration, but you never initialise an empty file at the beginning; perhaps this is the intentional behaviour, but I do not think so; anyway, you could do the redirection once for the whole for /F loop, either (over-)writing (>) or appending (>>), as you prefer;
  • the pipe into the type command is useless, because type does not accept any incoming data anyway; I guess the | symbol should have been replaced by &; nevertheless, the type command does not make sense in the loop, I think it should be executed after the loop instead to display the complete collected data once;
  • the echo( could be removed as it just writes a blank line to the console;
  • at the end the temporary VBScript file should be cleaned up;

Correcting all these things lead to a batch script like this (I do not write it all in a single line for readability):

> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" (
            > "%TEMP%\tmp.vbs" echo WScript.Echo "%%a" ^& " free=" ^& FormatNumber^(CDbl^(%%b^) / 1024 / 1024 / 1024, 2^) ^& " GiB" ^& " size=" ^& FormatNumber^(CDbl^(%%c^) / 1024 / 1024 / 1024, 2^) ^& " GiB"
            CScript //NoLogo "%TEMP%\tmp.vbs"
        )
    )
)
type "C:\test\test2.txt"
del "%TEMP%\tmp.vbs"

The whole approach could be improved though:

  • the temporary VBScript script could be written once rather than per each for /F loop iteration; for this to work you need to pass the variable values as command line arguments; consult this thread to learn how it works: Using command line arguments in VBscript;
  • using echo to write the temporary VBScript file requires lots of escaping, like ^&, for example; to avoid this, we could place the VBScript code in a block that is prefixed by ::::, which is seen as an invalid jump label by the batch file interpreter, and they could even be skipped by placing an exit /B just before them; this block can easily be extracted by findstr, and the colons can be removed by for /F;

So this is what I mean:

> "%TEMP%\tmp.vbs" (for /F "tokens=* delims=:" %%z in ('findstr /B "::::" "%~f0"') do @echo/%%z)
> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" CScript //NoLogo "%TEMP%\tmp.vbs" "%%a" "%%b" "%%c"
    )
)
type "C:\test\test2.txt"
del "%TEMP%\tmp.vbs"
exit /B

::::If WScript.Arguments.Count < 3 Then WScript.Quit 1
::::WScript.Echo WScript.Arguments.Item(0) & _
::::    " free=" & FormatNumber(CDbl(WScript.Arguments.Item(1)) / 1024 / 1024 / 1024, 2) & " GiB" & _
::::    " size=" & FormatNumber(CDbl(WScript.Arguments.Item(2)) / 1024 / 1024 / 1024, 2) & " GiB"

You could even avoid a temporary file holding the VBScript code when applying the technique demonstrated in this thread: Is it possible to embed and execute VBScript within a batch file without using a temporary file?
(Refer also to these Microsoft articles: Using Windows Script Files (.wsf), and How Come You Guys Don’t Use .WSF Files?.)

<!-- :Batch script section
> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" CScript //NoLogo "%~f0?.wsf" "%%a" "%%b" "%%c"
    )
)
type "C:\test\test2.txt"
exit /B

---- WSF script section -->
<job><script language="VBScript">
    If WScript.Arguments.Count < 3 Then WScript.Quit 1
    WScript.Echo WScript.Arguments.Item(0) & _
        " free=" & FormatNumber(CDbl(WScript.Arguments.Item(1)) / 1024 / 1024 / 1024, 2) & " GiB" & _
        " size=" & FormatNumber(CDbl(WScript.Arguments.Item(2)) / 1024 / 1024 / 1024, 2) & " GiB"
</script></job>

And here is yet another approach that does not use a temporary VBScript file, applying the method illustrated in this thread: HTA & Batch Hybrid, passing variables from BATCH section. The disadvantage of this is brief flickers of HTA windows appearing and disappearing.
(Refer also to this Microsoft article: HTML Applications (HTAs).)

<!-- ::Batch script section ----
> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" set "DeviceID=%%a" & set "FreeSpace=%%b" & set "Size=%%c" & MSHTA "%~f0"
    )
)
type "C:\test\test2.txt"
exit /B

---- ::HTA script section -->
<script language="VBScript">
    Set Env = CreateObject("WScript.Shell").Environment("Process")
    Set StdOut = CreateObject("Scripting.FileSystemObject").GetStandardStream(1)
    If Not Env("DeviceID") = "" Then
        StdOut.WriteLine(Env("DeviceID") & _
            " free=" & FormatNumber(CDbl(Env("FreeSpace")) / 1024 / 1024 / 1024, 2) & " GiB" & _
            " size=" & FormatNumber(CDbl(Env("Size")) / 1024 / 1024 / 1024, 2) & " GiB")
    End If
    Set StdOut = Nothing
    Set Env = Nothing
    Close()
</script>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.