Take required space from available space and store in a variable - batch-file

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
)

Related

Batch conditional FOR loop nested IF

I'm trying to write a script that parses a text file to identify removable drives by Label. The goal is to create a copy script based on the found drives.
I'm able to parse the file but I can't seem to get the second pair of IF conditions to be true.
I may be misunderstanding how variables are evaluated in each loop of the FOR.
Thoughts appreciated.
EDIT: Expanding on the original request for clarity(it was late when I asked) The expanded goal is to identify one or many target drives to copy data from an SD card. Since the drives letter allocations vary from PC to PC and depend on which is plugged in first my idea was to use the labels as somewhat identifiable values and create a simple copy string along the lines of
XCOPY SourceDrive\*.jpg DestinationDrive1\<GetDate>\
XCOPY SourceDrive\*.jpg DestinationDrive2\<GetDate>\
End Edit
#echo off
cls
title "Detecting device drive letters..."
setlocal enabledelayedexpansion
set DestinationDrive1Found=
set DestinationDrive2Found=
set SourceDriveFound=
set SourceDrive=
set DestinationDrive1=
set DestinationDrive2=
echo Detecting device drive letters...
echo list volume > %systemdrive%\ListDrives.tmp
diskpart /s %systemdrive%\ListDrives.tmp > %systemdrive%\CurrentDrives.tmp
echo Checking drive IDs
FOR /F "skip=7 tokens=2-4" %%a IN (%systemdrive%\CurrentDrives.tmp) DO (
set "matchvar=%%c"
set "comparevar=!matchvar:DRIVE=!"
IF /I NOT "!comparevar!" == "%%c" IF "!DestinationDrive1Found!" NEQ 1 (
echo Drive %%b was found to be %%c and will be DESTINATION1
set DestinationDrive1Found=1
set DestinationDrive1=%%b )
IF /I NOT "!comparevar!" == "%%c" IF "!DestinationDrive1Found!" EQU 1 ( echo Drive %%b was found to be %%c and will be DESTINATION2
set DestinationDrive2Found=1
set DestinationDrive2=%%b )
)
File I'm parsing
Microsoft DiskPart version 10.0.10586
Copyright (C) 1999-2013 Microsoft Corporation.
On computer: NEWGLOOMWIN10
Volume ### Ltr Label Fs Type Size Status Info
---------- --- ----------- ----- ---------- ------- --------- --------
Volume 0 D DVD-ROM 0 B No Media
Volume 1 E Macintosh H HFS Partition 231 GB Healthy
Volume 2 C BOOTCAMP NTFS Partition 721 GB Healthy System
Volume 3 F TEAMXCAMRAX FAT Removable 1937 MB Healthy
Volume 4 G TEAMXDRIVEX NTFS Partition 465 GB Healthy
Volume 5 H TEAMYDRIVEY NTFS Partition 931 GB Healthy
Thanks to all who responded. Had #Mofi provided an answer I would have marked that response as the answer. In the end the removal of a four quote marks and the addition of an ELSE got me the answer I was looking for.
The script below gives me exactly what I'm after.
#echo off
cls
title "Detecting device drive letters..."
setlocal enabledelayedexpansion
set DestinationDrive1Found=
set DestinationDrive2Found=
set SourceDriveFound=
set SourceDrive=
set DestinationDrive1=
set DestinationDrive2=
echo Detecting device drive letters...
echo list volume > %systemdrive%\ListDrives.tmp
diskpart /s %systemdrive%\ListDrives.tmp > %systemdrive%\CurrentDrives.tmp
echo Checking drive IDs
FOR /F "skip=7 tokens=2-4" %%a IN (%systemdrive%\CurrentDrives.tmp) DO (
set "matchvar=%%c"
set "comparevar=!matchvar:CAMRA=!"
IF /I NOT "!comparevar!" == "%%c" (#echo Drive %%b was found to be %%c and will be SOURCE
set SourceDriveFound=1
set SourceDrive=%%b)
)
echo Checking drive IDs
FOR /F "skip=7 tokens=2-4" %%a IN (%systemdrive%\CurrentDrives.tmp) DO (
set "matchvar=%%c"
set "comparevar=!matchvar:DRIVE=!"
IF /I NOT "!comparevar!" == "%%c" IF !DestinationDrive1Found! NEQ 1 (
echo Drive %%b was found to be %%c and will be DESTINATION1
set DestinationDrive1Found=1
set DestinationDrive1=%%b
) ELSE (
IF /I NOT "!comparevar!" == "%%c" IF !DestinationDrive1Found! EQU 1 ( echo Drive %%b was found to be %%c and will be DESTINATION2
set DestinationDrive2Found=1
set DestinationDrive2=%%b ))
)
If you just want to set some variables to the drives allocated to any removable drive with the string DRIVE in it's 'label' then the following may provide you with an alternative not requiring elevation.
#Echo Off
For /F "UseBackQ Skip=1 Delims=" %%A In (`WMIC LogicalDisk Where^
"DriveType='2' And VolumeName Like '%%DRIVE%%'" Get Name`
) Do For /F %%B In ("%%A") Do Set/A "i+=1"&Call Set "_drv%%i%%=%%B"
Set _drv
Timeout -1
[Edits] - an example correctly stipulating a fixed drive
#Echo Off
For /F "UseBackQ Skip=1 Delims=" %%A In (`WMIC LogicalDisk Where^
"DriveType='3' And VolumeName Like '%%DRIVE%%'" Get Name`
) Do For /F %%B In ("%%A") Do Set/A "i+=1"&Call Set "_drv%%i%%=%%B"
Set _drv
Timeout -1
An example without stipulating the type of drive, (recommended based on comments).
#Echo Off
For /F "UseBackQ Skip=1 Delims=" %%A In (
`WMIC LogicalDisk Where "VolumeName Like '%%DRIVE%%'" Get Name`
) Do For /F %%B In ("%%A") Do Set/A "i+=1"&Call Set "_drv%%i%%=%%B"
Set _drv
Timeout -1

Copying random folders stopping to soon

I'm making a script that copies random folders from source to destination until that folder is full (knowing that the destination folder is smaller then the source.
All the individual parts are working as intended, but I'm now running the script without the copy included (just an echo) so basically if there aren't any folders larger then the remaining space in the destination directory, it should be running for ever.
But it stops at an average of 5 iterations while there isn't any folder large enough to fill up the directory. This is the code I'm using
EDIT: got it olmost working, but now sometimes a needed_space outputs 0
#ECHO off
setlocal EnableDelayedExpansion
SET n=0
SET SOURCE_PATH=M:\Movies
SET DEST_PATH=E:\Movies
:: get all folders in dir
FOR /f "usebackq tokens=*" %%a in (`DIR /b/a:d %SOURCE_PATH%`) do (
SET /A n+=1
SET folder[!n!]=%%a
)
:loop
:: selecting ranodm number
SET /A rand=(n*%random%)/32768+1
:: check for space
for /F "tokens=3" %%S in ('dir /-c "%SOURCE_PATH%\!folder[%rand%]!\*" ^| findstr /c:"File(s)"') DO set NEEDED_SPACE=%%S
FOR /f "tokens=1*delims=:" %%i IN ('fsutil volume diskfree %DEST_PATH%') DO SET FREE_SPACE=%%j
ECHO %FREE_SPACE% - %NEEDED_SPACE%
IF %NEEDED_SPACE% GTR %FREE_SPACE% GOTO done
:: check if file does not exits
IF EXIST %DEST_PATH%\!folder[%rand%]! GOTO loop
:: copy file
ECHO moving %SOURCE_PATH%\!folder[%rand%]! to %DEST_PATH%\!folder[%rand%]!
:: again
GOTO loop
:done
ECHO Done copying random folders, have fun!
1.- You dont have taken in consideration the environment space exhausting. Maybe you can not create the array you are intending.
2.- fsutil volume diskfree ... always return the space in VOLUME, no matter if you indicate a directory
3.-if command do numeric comparisons ONLY when all the characters in both sides of the operator are numeric. Your () disables it, so (10) is less than (3)
Ok, this is working so far. waiting for the disk to get full if that works, this should be sufficient (not 100% perfect check but close enough)
code:
#ECHO off
setlocal EnableDelayedExpansion
SET n=0
SET SOURCE_PATH=M:\src
SET DEST_PATH=E:\dest
:: get all folders in dir
FOR /f "usebackq tokens=*" %%a in (`DIR /b/a:d %SOURCE_PATH%`) do (
SET /A n+=1
SET folder[!n!]=%%a
)
:loop
:: selecting ranodm number
SET /A rand=(n*%random%)/32768+1
:: check for space
for /F "tokens=3" %%a in ('dir /-c "%SOURCE_PATH%\!folder[%rand%]!" ^| findstr /c:"File(s)"') do set bytesfree=%%a
set bytesfree=%bytesfree:,=%
set /a NEEDED_SPACE=%bytesfree:~0,-3%
for /f "tokens=3" %%a in ('dir %DEST_PATH%\') do set bytesfree=%%a
set bytesfree=%bytesfree:,=%
set /a FREE_SPACE=%bytesfree:~0,-3%
ECHO %FREE_SPACE% - %NEEDED_SPACE%
IF %NEEDED_SPACE% GTR %FREE_SPACE% GOTO done
:: check if file does not exits
IF EXIST %DEST_PATH%\!folder[%rand%]! GOTO loop
:: copy file
set src="%SOURCE_PATH%\!folder[%rand%]!\*"
set dest= "%DEST_PATH%\!folder[%rand%]!\*"
ECHO moving %src% to %dest%
xcopy /s /e /i %src% %dest%
:: again
GOTO loop
:done
ECHO Done copying random folders, have fun!

Division: overcoming 2 GB limitation of batch math

I have a output.txt file which has following content:
Windows 6543765432
Linux 4534653463
MacOS 3564325
Ubuntu 8235646255
I want to create a batch script which searches for all numeric values in output.txt and divide them by 1024 (only integer part required in result not decimal places ) sothat memory in KB can be changed into MB (overcoming 2 GB limitation of batch math )
Trying with below but no output..
#echo off
setlocal enabledelayedexpansion
(for /f "tokens=1,2 delims=" %%a in (output.txt) do (
set /a MB=!b!/1024
echo %%a !MB!
))
You can do that with pure Batch splitting the number in two groups of 5 digits each, and operating each group accordingly:
#echo off
setlocal EnableDelayedExpansion
(for /f "tokens=1,2" %%a in (output.txt) do (
call :KBtoMB %%b MB=
echo %%a !MB!
))
goto :EOF
:KBtoMB KB MB=
set KB=%1
set KBhigh=%KB:~0,-5%
set KBlow=%KB:~-5%
for /L %%i in (1,1,4) do if "!KBlow:~0,1!" equ "0" set KBlow=!KBlow:~1!
set /A %2=KBhigh/1024*100000 + (KBhigh%%1024*100000+KBlow)/1024
exit /B
As a matter of fact, you may divide a number with unlimited number of digits this way!
#if (#CodeSection == #Batch) #then
#echo off
setlocal enabledelayedexpansion
set JScall=Cscript //nologo //E:JScript "%~F0"
for /f "tokens=1,2" %%i in (output.txt) do (
for /f %%a in ('%JScall% "%%j/1024"') do set a=%%a
for /f "delims=." %%z in ("!a!") do set a=%%z
echo %%i !a!>>newout.txt
)
)
goto :EOF
#end
WScript.Echo(eval(WScript.Arguments.Unnamed.Item(0)));
try that?... ;)
gawk can be easily for any text hanlding from simple to complex
gawk " {printf(\"%s %d\n\", $1 ,$2/1024)}" output.txt
and it works fine under any OS include windows, linux.
C:\dos>gawk " {printf(\"%s %d\n\", $1 ,$2/1024)}" output.txt
Windows 6390395
Linux 4428372
MacOS 3480
Ubuntu 8042623

String Variables not setting in For Loop

I think I have a simple problem. I have a file where I'm trying to format the output into columns. I think I found a way to do it here but the string1 and string2 variables are not setting. Any ideas? I'm not familiar with Windows scripting and come from Linux where it's much easier.
Make this:
San Disk USB Drive, 10-12-2013
Superdrive Disk USB Drive, 10-11-2013
look like this:
San Disk USB Drive 10-12-2013
Superdrive Disk USB Drive 10-11-2013
Code I'm using to read the text file:
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ temp_sorted_usb_history.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!"
echo(!var!
for /F "tokens=1,2 delims=," %%b in ("!var!") do (
echo %%b%%c
set string1=%%b
set string2=%%c
set string1="%string1% "
set string2="%string2% "
echo !string1:~-40! !string2:~-40!
)
ping -n 4 -w 1 127.0.0.1 >NUL
ENDLOCAL
)
The problem is that I can't get the string variables to set.
I don't understand why you are using findstr command. This is the way I would do that:
setlocal EnableDelayedExpansion
for /F "tokens=1,2 delims=," %%b in (temp_sorted_usb_history.txt) do (
echo %%b%%c
set "string1=%%b "
set "string2=%%c "
echo !string1:~0,40! !string2:~0,40!
)
ping -n 4 -w 1 127.0.0.1 >NUL
With setlocal enabledelayedexpansion
variables in parentheses are referred to as !variable! instead of %variable%
You have two strings that do not use !variable! notation, and they should.
You could however use this style of syntax where the quotes are not actually part of the variable, and eliminate the two other lines.
set "string1=%%b "
set "string2=%%c "
Another option might be to have the date first, and sorting by date would be simple.
#echo off
for /F "tokens=1,2 delims=," %%b in (temp_sorted_usb_history.txt) do (
echo %%c - %%b
)

How can I convert this raw bytes output to GB?

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

Resources