How can I convert this raw bytes output to GB? - batch-file

I'm using the below code to output the current free space on the C: Drive. How can I convert the output from bytes to GB using batch?
#echo off
for /f "usebackq delims== tokens=2" %%x in (`wmic logicaldisk where "DeviceID='C:'" get FreeSpace /format:value`) do set FreeSpace=%%x
echo %FreeSpace%

Batch does not support float point arithmetic. This would be a nice workaround:
#setlocal enableextensions enabledelayedexpansion
#echo off
for /f "usebackq delims== tokens=2" %%x in (`wmic logicaldisk where "DeviceID='C:'" get FreeSpace /format:value`) do set FreeSpace=%%x
echo !FreeSpace:~0,-10!,!FreeSpace:~2,-8!GB
It only works if you run the .bat as administrator. It just inserts a dot after the 9. digits from the right, and trims the last 7. This is not exactly matching the value from windows, because 1k is here 1000 and not 1024
A better but more complex solution would be to use VBScript, described in the following article: Article

Here is a solution that gives GB in a whole number. May not be what you wanted, but it was easy to do, and may do the trick for what you need. I couldn't really get it to work for me using wmic, but wmic is probably better than dir.
#setlocal enableextensions enabledelayedexpansion
#echo off
for /f "tokens=3" %%a in ('dir c:\') do (
set bytesfree=%%a
)
set bytesfree=%bytesfree:,=%
endlocal && set bytesfree=%bytesfree%
rem truncating end. loses precision
set /a kb=%bytesfree:~0,-3%
set /a mb = kb/1024
set /a gb = mb/1024
echo %gb%
Eh, well, here is the same thing using wmic.
#setlocal enableextensions enabledelayedexpansion
#echo off
for /f "usebackq delims== tokens=2" %%x in (`wmic logicaldisk where "DeviceID='C:'" get FreeSpace /format:value`) do set FreeSpace=%%x
rem truncating end. losing precision
set /a kb=%FreeSpace:~0,-4%
set /a mb = kb/1024
set /a gb = mb/1024
echo %gb%

Must you use batch commands? Can you not use PowerShell?
[System.IO.DriveInfo]::GetDrives() | Where {$_.Name -eq 'C:\'} |
Select {$_.AvailableFreeSpace/1GB}

REM ECHO Disk Storage
for /f "tokens=1" %%d in (
'wmic logicaldisk where drivetype^=3 get deviceid ^| find ":"') do (
for /f "usebackq delims== tokens=2" %%x in (`wmic logicaldisk where "DeviceID='%%d'" get Size /value`) do set Size=%%x
echo VolumeSize on %%d Partition = !Size:~0,-10!,!Size:~2,-8! GB >output.txt
for /f "usebackq delims== tokens=2" %%x in (`wmic logicaldisk where "DeviceID='%%d'" get FreeSpace /value`) do set FreeSpace=%%x
echo Freespace on %%d Partition = !FreeSpace:~0,-10!,!FreeSpace:~2,-8! GB >> output.txt
echo.
)
)

Could use the StrFormatByteSize64() API function to convert a long int to human readable size. This results in a more accurate value than truncating to meet the cmd environment's 32-bit limit. This API function supports values in the ranges from bytes and kilobytes to petabytes and exabytes.
(This script is a hybrid Batch + PowerShell script. Save it with a .bat extension.)
<# : batch portion
#echo off & setlocal
for /f "tokens=2 delims==" %%x in (
'wmic logicaldisk where "DeviceID='C:'" get FreeSpace /value'
) do call :int2size FreeSpace %%x
echo %FreeSpace% free on C:
rem // end main runtime
exit /b
rem // batch int2size function
:int2size <return_varname> <int>
setlocal
set "num=%~2"
for /f "delims=" %%I in (
'powershell -noprofile "iex (${%~f0} | out-string)"'
) do endlocal & set "%~1=%%I" & goto :EOF
: end batch / begin PowerShell #>
Add-Type #'
using System.Runtime.InteropServices;
namespace shlwapi {
public static class dll {
[DllImport("shlwapi.dll")]
public static extern long StrFormatByteSize64(ulong fileSize,
System.Text.StringBuilder buffer, int bufferSize);
}
}
'#
$sb = new-object Text.StringBuilder 16
[void][shlwapi.dll]::StrFormatByteSize64($env:num, $sb, 16)
$sb.ToString()

Related

How to rename a variable in a batch script and keep the data

I’m trying to parse some information from an XML file with a batch script and I have not been able to figure out how to rename the variable and keep the data intact. The output file is HTML which is what the device that is reading the data needs.
The batch file creates the HTML file just fine. The device is looking for Temperature: 75 and that is what it looks like in the file however if you check the file with a HTML editor the search name is included in the variable and the device can't use the data.
This is what I have been working on;
#echo off
start /b /WAIT %~dp0bitsadmin.exe /transfer "currentstats"
https://w1.weather.gov/xml/current_obs/KMGM.xml %~dp0Weather.xml
for %%a in (%~dp0Weather.xml) do (
for /f "tokens=* " %%b in ( ' type "%%a" ^|findstr /i "temp_f" ' ) do set tem1="%%b"
for /f "tokens=* " %%b in ( ' type "%%a" ^|findstr /i "relative_humidity" ' ) do set
hum1="%%b"
)
for /f "tokens=* delims=" %%P in ("<HTML>") do ( #echo %%P > %~dp0Weather.html)
for /f "tokens=* delims=" %%P in (%tem1%) do ( #echo Temperature: %%P >> %~dp0Weather.html)
for /f "tokens=* delims=" %%P in ("<BR />") do ( #echo %%P >> %~dp0Weather.html)
for /f "tokens=* delims=" %%P in (%hum1%) do ( #echo Humidity: %%P >> %~dp0Weather.html)
for /f "tokens=* delims=" %%P in ("</HTML>") do ( #echo %%P >> %~dp0Weather.html)
Data sample (from comments) Line breaks assumed.
<HTML> Temperature: <temp_f>75.0</temp_f> <BR />
Humidity: <relative_humidity>62</relative_humidity>
</HTML>
I Can’t figure how to remove the Left & Right pointing single angle quotation marks and what is between them so I end up with this.
<HTML> Temperature: 75.0 <BR />
Humidity: 62
</HTML>
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the source directory and filename are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q74092241.txt"
SET "omit="^<temp_f^>" "^</temp_f^>" "^<relative_humidity^>" "^</relative_humidity^>""
FOR /f "usebackqdelims=" %%e IN ("%filename1%") DO (
SET "line=%%e"
FOR %%y IN (%omit%) DO SET "line=!line:%%~y=!"
echo !line!
)
GOTO :EOF
I believe the key is to "escape" (turn off the special meaning) of >|< with a caret ^
It is much simpler to define what data you want to keep, rather than what data you want to remove! Just do this:
#echo off
setlocal EnableDelayedExpansion
rem Define keep values:
set "HTML=<HTML>"
set "BR /=<BR />"
set "/HTML=</HTML>"
(for /F "delims=" %%a in (Weather.xml) do (
set "line=%%a"
set "line=!line:<=%%!"
call set "line=!line:>=%%!"
echo !line!
)) > Weather.html
Change any <name> by %name% and evaluate the result line. All %varNames% that don't exists will be deleted...
I would consider doing this using PowerShell instead. Using PowerShell you should be able to grab the data directly instead of downloading and reading an XML file. It also affords you the opportunity to output directly to HTML.
Example:
WeatherData.ps1
$weather_url = "https://w1.weather.gov/xml/current_obs/KMGM.xml"
$request = [System.Net.HttpWebRequest]::Create($weather_url)
$request.UserAgent = "MyApplication/v1.0 (http://foo.bar.baz; foo#bar.baz)"
$response = $request.GetResponse()
$doc = New-Object System.Xml.XmlDocument
$doc.Load($response.GetResponseStream())
$doc.current_observation | ConvertTo-HTML -As List #{Label = 'Temperature'; Expression = {[Int]$_.temp_f}}, #{Label = 'Humidity'; Expression = {$_.relative_humidity}} | Out-File ($MyInvocation.MyCommand.Path.DirectoryName + 'weather.html')
To run that from a batch file, use this, (obviously changing %UserProfile%\Desktop\ as needed):
WeatherTest.cmd
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy RemoteSigned -File "%UserProfile%\Desktop\WeatherData.ps1"
The output file weather.html should be located in the cmd.exe instance's current directory.
Note: If you want the decimal temperature, as opposed to the whole integer, as I used above, change [Int]$_.temp_f to just $_.temp_f.
If I chose to do this using only a batch file, then I'd use the built-in curl.exe utility, instead of bitsadmin.exe:
WeatherData.cmd
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "_=" & For /F "Tokens=3 Delims=<>" %%G In ('%SystemRoot%\System32\curl.exe
"https://w1.weather.gov/xml/current_obs/KMGM.xml" -H "Accept: Application/XML"
2^>NUL ^| %SystemRoot%\System32\findstr.exe /IL "<temp_f> <relative_humidity>"
') Do #(If Not Defined _ (Set /P "=<HTML>Temperature: %%G<BR />" 0<NUL
Set "_=.") Else Set /P "=Humidity: %%G</HTML>" 0<NUL & Echo(
) 1>>"%~dp0weather.html"
Here's a modification which is of no purpose to any future user, and only included for the OP:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "_=" & For /F "Tokens=3 Delims=<>" %%G In ('%SystemRoot%\System32\curl.exe
"https://w1.weather.gov/xml/current_obs/KMGM.xml" -H "Accept: Application/XML"
2^>NUL ^| %SystemRoot%\System32\findstr.exe /IL "<temp_f> <relative_humidity>"
') Do #(If Not Defined _ (Set /P "=<HTML>Temperature: %%G<BR />" 0<NUL
Set "_=.") Else Echo(&Echo Humidity: %%G&Echo ^</HTML^>
) 1>>"%~dp0weather.html"

Get only numbers in version from dll file

I'm trying to get the vertion of a .dll file, but only the number of the version.
I find the code
wmic datafile where name='C:\\...\\MY_FILE.dll' get version
This code returns:
Version
3.56.0.1
I need the return to be only '3.56.0.1' and this could be saved in a variable for i'll can call in a echo after.
set var="HOW DO I DO?"
echo %var%
How can I get this?
I can use the code below too, but in this format I think its harder
wmic datafile where name='C:\\...\\MY_FILE.dll' get version /format:list
This code returns:
Version=3.56.0.1
And similarly to those posted, with minor differences.
A batch-file:
#For /F "Delims=" %%A In ('WMIC DataFile Where "Name='C:\\...\\MY_FILE.dll'" Get Version /Value 2^>Nul')Do #For /F "Tokens=*" %%B In ("%%A")Do #Set "%%B"
#Echo(%Version%&Pause
A cmd version:
For /F "Delims=" %A In ('WMIC DataFile Where "Name='C:\\...\\MY_FILE.dll'" Get Version /Value 2^>Nul')Do #For /F "Tokens=*" %B In ("%A")Do #Set "%B"
Where the variable %Version% should be set to the local environment.
You'll need to parse with a for /f.
And another for /f to repair broken WMIC output.
On cmdline (using a windows dll):
For /f "usebackqdelims=" %A in (`wmic datafile where name^='C:\\Windows\\System32\\authui.dll' get version /format:list^|findstr "Version"`) do #For /F "delims=" %B in ("%A") do #Set "%B"
set Version
Version=10.0.17134.1
In a batch file double the percent signs of the for meta variable(s)
So you would require a for loop to do this.. see more help on it by running for /? from cmd.exe
#echo off
for /f "tokens=1,* delims==" %%i in ('wmic datafile where name^="C:\\...\\MY_FILE.dll" get version /format:list') do if not defined var set var=%%j
echo %var%
Note that the help will not assist you with the caret escape character used on the = which however you can find help on here at SO.
You should use:
#echo off
for /F "delims== tokens=2" %%A IN ('wmic datafile where name^="C:\\...\\MY_FILE.dll" get version /format:list') do (
for %%B IN ("%%A") do set "var_name=%%~B"
)
echo %var_name%
which loops through your command and finds the second token according to the delimeter specified (=).
Due to wmic unusual line endings (<CR><CR><LF>), you should use a double for loop.
If you scripted in PowerShell, you could use:
$var = [Diagnostics.FileVersionInfo]::GetVersionInfo('C:/.../MY_FILE.dll').ProductVersion
If you want to do it in a .bat file script, there is some overhead.
C:>TYPE gv.bat
#ECHO OFF
FOR /F "delims=" %%v IN ('PowerShell -NoLogo -NoProfile -Command ^
"[Diagnostics.FileVersionInfo]::GetVersionInfo('C:/Windows/System32/dfscli.dll').ProductVersion"') DO (
SET "var=%%~v"
)
ECHO var is +++%var%+++
C:>CALL gv.bat
var is +++10.0.17763.1+++

Batch variables remove line break

setlocal enabledelayedexpansion
... a lot of batch script ...
IF ... a lot of batch script ...
for /f "skip=2 delims== tokens=2" %%g in ('WMIC DATAFILE WHERE ^"Name^='!filePath!'^" GET Version /value') DO (
set fileVersion=%%g
)
ECHO Version: !fileVersion!
set nightlyBuildFullPath=%2\Setups\Nightly Builds\!fileVersion!\Full\
ECHO FullPath: !nightlyBuildFullPath!
Result:
Version: 1.5.1810.202
FullPath: C:\test\Setups\Nightly Builds\1.5.1810.202
\Full\
I want to remove the linebreaks in the fileVersion variable because I have to build a path for later use. If I actually build the path string I have a linebreak in it, which corrupts my path.
ECHO Version: "%fileVersion:~0,-1%"
may assist your investigations...
Try like this:
for /f "skip=2 delims== tokens=2" %%g in ('WMIC DATAFILE WHERE ^"Name^='!filePath!'^" GET Version /value') DO (
for /f "tokens=* delims=" %%# in ("%%g") do set "fileVersion=%%#"
)
Put the following line into a file named veronly.ps1. Change the file name to the one you want to examine.
Get-WmiObject -Class 'CIM_DataFile' -Filter "name='C:\\Program Files (x86)\\PuTTY\\psftp.exe'" | % { $_.Version }
Put the following into the .bat script file.
FOR /F %%v IN ('powershell -NoLogo -NoProfile -File .\veronly.ps1') DO (SET "THEVERSION=%%v")
ECHO %THEVERSION%
REM :: As you use the commutator '/Value', you can use it to parse for command.
set myParams = WMIC DATAFILE WHERE ^"Name^='!filePath!'^" GET Version /value
for /f "tokens=1-2 skip=1 delims==" %%i in ('wmic /node:"%1" %myParams%') do if "%%i" == "Version" set "myVers=%%j"
REM :: DOS commands have the advantage of being able to run natively on all Microsoft systems. This can be useful when you have obsolete systems or when system or network security limits the execution of system commands.

Take required space from available space and store in a variable

Ok,I have this code:
echo [%time%] Beginning install
echo [%time%] Checking hard drive C:\
for /f "usebackq tokens=* delims=" %%# in (`"wmic logicaldisk where name='C:' get FreeSpace /format:value"`) do (
for /f "tokens=* delims=" %%a in ("%%#") do #set "%%a"
)
echo [%time%] Available space: %freespace% Bytes
echo [%time%] Required space: 34 Megabytes
#set requiredspace=34
#set /a "available_required=%freespace%-%requiredspace%"
echo [%time%] %available_required%
pause
It finds the available storage on a hard drive and takes away the required to find out if there is enough storage to complete the install, unfortunately, Windows CMD doesn't like numbers higher than 32 bit so I need to cut it down to megabytes, the maths cant be done on the stored variable so It has to be found directly from the command.
For example, (Obviously not working though)
"wmic logicaldisk where name='C:' get FreeSpace /format:value /division:megabytes"
Thanks
Instead of utilising WMIC, you could leverage powershell for your freespace calculation directly into MegaBytes:
#Echo Off
For /F "Delims=" %%A In ('Powershell "gWMI Win32_LogicalDisk"^
"-F ""DeviceID='C:'""""|%%{[Math]::Round($_.FreeSpace/1MB,0)}"'
) Do Set "FDS=%%A"
Echo=Freespace on C: is %FDS%MB
Timeout -1
If you were looking for GB to two decimal places then a quick edit of the above could achieve that too:
#Echo Off
For /F "Delims=" %%A In ('Powershell "gWMI Win32_LogicalDisk"^
"-F ""DeviceID='C:'""""|%%{[Math]::Round($_.FreeSpace/1GB,2)}"'
) Do Set "FDS=%%A"
Echo=Freespace on C: is %FDS%GB
Timeout -1
Quick-and-dirty:
set "freespace=%freespace:~0,-6%"
echo [%time%] Available space: %freespace% MBytes
Divides freespace by 1,000,000 rather than 1024^2
You can pad the values and compare them as strings
set "rSpace=00000000000035651584"
set "fSpace=00000000000000000000%freeSpace%"
if "%rSpace:~-20%" gtr "%fSpace:~-20%" (
echo [%time%] Not enough space available
) else (
echo [%time%] Enough Space available
)

Split a file using windows batch script

I have a csv file and i need to split it in to n files such that each split file should not exceed 100 mb. I need to achieve it in windows batch script. I tried the below way but its taking lot of time as my unsplit file is in GBs
#echo off
setlocal enableextensions enabledelayedexpansion
set count=1
set maxbytesize=100000000
set size=1
type NUL > output_1.csv
FOR /F "tokens=*" %%i in (myfile.csv) do (
FOR /F "usebackq" %%A in ('!filename!_!count!.csv') do (
set size=%%~zA)
if !size! LSS !maxbytesize! (
echo %%i>>!filename!_!count!.csv) else (
set /a count+=1
echo %%i>>!filename!_!count!.csv
))
please let me know if there is a better way to achieve this. I cant go to any other scripting languages as my server is windows
This would do the trick assuming your lines are roughly the same size.
Its advantage is that it is only a 2 pass solution, One for counting the lines and the other for printing them.
#rem echo off
#rem usage: batchsplit.bat <file-to-split> <size-limit>
#rem it will generate files named <file-to-split>.part_NNN
setlocal EnableDelayedExpansion
set FILE_TO_SPLIT=%1
set SIZE_LIMIT=%2
for /f %%s in ('dir /b %FILE_TO_SPLIT%') do set SIZE=%%~Zs
for /f %%c in ('type "%FILE_TO_SPLIT%"^|find "" /v /c') do set LINE_COUNT=%%c
set /a AVG_LINE_SIZE=%SIZE%/%LINE_COUNT%
set /a LINES_PER_PART=%SIZE_LIMIT%/%AVG_LINE_SIZE%
set "cmd=findstr /R /N "^^" %FILE_TO_SPLIT%"
for /f "tokens=1,2* delims=:" %%a in ('!cmd!') do #(
set /a ccc = %%a / %LINES_PER_PART%
echo %%b >> %FILE_TO_SPLIT%.part_!ccc!
)
save it as batchsplit.bat and run it using:
batchsplit.bat myfile.csv 100000000

Resources