Batch files compare numbers - command-line

I have to write a script that check if there is enough space in the drive.
I wrote the following:
#echo off
set gbsize = 1,073,741,824
Set gbsize=%gbsize:,=%
for /f "tokens=3" %%a in ('dir c:\') do (
set /a bytesfree=%%a
)
set bytesfree=%bytesfree:,=%
endlocal && set bytesfree=%bytesfree%
If %gbsize% gtr %bytesfree% echo hi
but when the script thrown error:
Invalid number. Numeric constants are either decimal (17),
hexadecimal (0x11), or octal (021).
what did I miss? can anyone help?
thanks!

Your immediate problem is attempting to use SET /A when there are commas in your value.
You also need to remove the spaces before and after = when you define gbsize.
I don't understand why you have endlocal without a setlocal
I believe this is what you were looking for. It will echo "hi" if there is less then 1GB free space.
#echo off
setlocal
set gbsize=1,073,741,824
Set gbsize=%gbsize:,=%
for /f "tokens=3" %%a in ('dir c:\') do set bytesfree=%%a
set bytesfree=%bytesfree:,=%
If %gbsize% gtr %bytesfree% echo hi
Another, more direct, (and possibly more accurate?) way to get the free space
for /f "tokens=2" %%S in ('wmic volume get DriveLetter^, FreeSpace^|find "C:"') do set bytesfree=%%S

Related

How can I compare two dates in batch?

I have a batch script to get two dates; one of a folder, and the current system date.
I want to specifically compare the two by seeing if the date of the folder is 10 minutes or less older than the current date. This essentially checks if the user has modified this folder at the most 10 minutes ago.
Here's my current code (not complete, but the base):
#echo off
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%a/%%b/%%c)
for /f "tokens=1-2 delims=/:" %%a in ('time /t') do (set mytime=%%a:%%b)
setlocal enabledelayedexpansion
if exist "C:\$Recycle.Bin" (
pushd "C:\$Recycle.Bin"
for /F "delims=" %%a in ('dir /S /b S-1-*-1001 /AD') do (set "{recycle-bin-date}=%%a")
for %%a in ("!{recycle-bin-date}!") do (
Set "data=%%~ta"
)
popd
)
set date1=%mydate% %mytime%
set date2=!data!
echo Date 1 (Current): %date1%
echo Date 2 (Recycle): %date2%
pause
::We have the dates above, how do I achieve what I'm trying to do?
If anyone could help me here, I'd really appreciate it.
You CAN do date time math in pure batch but it is quite cumbersome.
(See Ritchie Lawrence batch function library)
I recommend to use PowerShell as a tool for this
PowerShell one liner:
[int]([datetime]::Now - (gci 'c:\$Recycle.BIN\S-1-*1001' -Force).LastWriteTime).TotalMinutes
Wrapped in a batch
#Echo off
For /f "usebackq" %%A in (`
Powershell -NoP -C "[int]([datetime]::Now - (gci 'c:\$Recycle.BIN\S-1-*1001' -Force).LastWriteTime).TotalMinutes"
`) Do Set "AgeMinutes=%%A"
Echo Age in minutes %AgeMinutes%

Workaround for problems with "!" literals in a For loop with delayed variable expansion

How do I make a workaround for the FOR Loop? The problem is that DelayedExpansion fails for filenames with "!" in them
The code (this is part of a bigger script)
:FileScan
::Sets the filenames(in alphabetical order) to variables
SETLOCAL EnableDelayedExpansion
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
::Sets the foldernames(in alphabetical order) to variables
SET "FolderCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n /a:d "%~dp0Put_Your_Files_Here" 2^>nul') DO (
SET /a "FolderCount+=1"
SET Folder!FolderCount!=%%x
)
If not possible in batch how do I do it in PowerShell
to something that can be called by the original batch like:
:FileScan
Call %~dp0FileScanScript.ps1
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
Call %~dp0FolderScanScript.ps1
EDIT: CALL SET fixed the original code avoiding DelayedExpansion altogether
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
CALL SET "Ep%%EpCount%%=%%x"
)
The easiest solution is indeed to use callset to introduce another parsing phase:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%x"
)
popd
endlocal
However, this causes problems as soon as carets ^ appear in the strings or file names, because call doubles them when they appear quoted.
To solve this, you need to make sure that the strings are expanded in the second parsing phase too, which can be achieved by using an interim variable, like this:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set "Episode=%%x"
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%Episode%%"
)
popd
set Ep
endlocal
In addition, I added the filter option /A:-D to dir to ensure that no directories are returned. Furthermore, I used pushd and popd to change to the parent directory of the script temporarily. The dir command line as you wrote it, searched files *.mkv* in the parent directory of the script, but all the other ones in the current working directory, which is probably not what you wanted.
Another option is to toggle delayed expansion, so that the for variable reference %%x becomes expanded when delayed expansion is disabled, and the assignment to the Ep array-style variables is done when it is enabled. But you need to implement measures to transport variable values beyond the endlocal barrier then, like in the following example:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
rem /* Delayed expansion is disabled at this point, so expanding
rem the `for` variable reference `%%x` is safe here: */
set "Episode=%%x"
set /A "EpCount+=1"
rem // Enable delayed expansion here:
setlocal EnableDelayedExpansion
rem /* Use a `for /F` loop that iterates once only over the assignment string,
rem which cannot be empty, so the loop always iterates; the expanded value
rem is stored in `%%z`, which must be assigned after `endlocal` in order
rem to have it available afterwards and not to lose exclamation marks: */
for /F "delims=" %%z in ("Ep!EpCount!=!Episode!") do (
endlocal
set "%%z"
)
)
popd
set "Ep"
endlocal
CALL SET Ep%%EpCount%%=%%x
would set your variables appropriately, as would
FOR /f "tokens=1*delims=[]" %%x IN (
'Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul ^|find /v /n ""'
) DO (
SET /a epcount=%%x
CALL SET ep%%x=%%y
)
(prefix each name with [num] then use for to extract num to %%x and name to %%y (assuming there are no names that start [ or ]))
Note: The OP had originally tagged the question powershell in addition to batch-file, but later removed that tag (since restored).
The equivalent of the following batch-file (cmd) loop:
SETLOCAL EnableDelayedExpansion
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
in PowerShell (PSv3+) is:
# Collect all episode paths in array $Eps
$Eps = (Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv, *.mp4, *.avi, *.flv).FullName
# Count the number of paths collected.
$EpCount = $Eps.Count
The above will work with paths that have embedded ! instances too.
$Eps will be an array of the full filenames.
To then process the matching files one by one:
foreach ($ep in $Eps) { # Loop over all files (episodes)
# Work with $ep
}
Using a single array rather than distinct $Ep1, $Ep2, ... variables makes for much simpler and more efficient processing.
The above is the fastest way to process the already-collected-in-memory filenames one by one.
A more memory-efficient (though slightly slower), pipeline-based approach would be:
Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv,*.mp4,*.avi,*.flv | ForEach-Object {
# Work with $_.FullName - automatic variable $_ represents the input object at hand.
}

Batch file for listing all files from a date interval

I need to do a batch file which order all files by a date interval, example:
#echo off
echo Input the date(dd/mm/yyyy):
set /p compDate=
::After that I will compare from the actual day (%date%), example:
set interval = %compDate% - %date%... *Something like that*
::After that I need to list all files from a specific directory, example:
echo Input the directory:
set /p directory=
SET Exit= %UserProfile%\Desktop\test.txt
::After that I might need dir /tc to get the creation date, example:
pushd "%directory%"
dir /s /tc /a-d > %Exit%
::After that I don't know how to get only the lines which are in date interval, example:
Today is 19/08/2014, but I want to search all files created from day 10/07/2014.
So I have to copy all lines which have the date 10/07/2014, 11/07/2014, 12/07/2014 and so on until stop on today created files.
I tried with findstr, but I can't set the date interval, just a specific date to search in the .txt created.
Somebody know how to do that?
If I correctly understood the request, you really don't want files created in a given interval, but files created after a given date. The Batch file below assume that the date used by the system appear in DD/MM/YYYY order:
EDIT: Some modifications as reply to the comments
#echo off
setlocal EnableDelayedExpansion
echo Input the date(dd/mm/yyyy):
set /p compDate=
for /F "tokens=1-3 delims=/" %%a in ("%compDate%") do set compDate=%%c%%b%%a
echo Input the directory:
set /p directory=
SET Exit=%UserProfile%\Desktop\test.txt
pushd "%directory%"
(for /F "tokens=1-5*" %%a in ('dir /s /od /tc /a-d') do (
set "fileDate=%%a"
if "!fileDate:~2,1!!fileDate:~5,1!" equ "//" (
for /F "tokens=1-3 delims=/" %%x in ("!fileDate!") do set fileDate=%%z%%y%%x
if !fileDate! geq %compDate% (
set "fileSize= %%e"
echo %%a %%b %%c %%d !fileSize:~-16! %%f
)
)
)) > %Exit%
popd
A solution that uses WMIC and is independent from time/date settings:
#echo off
setlocal
set /p compDate=Input the date(yyyymmdd):
set /p directory=Full directory path (with no slash at the end):
set exit_file= %UserProfile%\Desktop\test.txt
break>%exit_file%
for /f "tokens=1,2 delims=:" %%a in ("%directory%") do (
set "dir_drive=%%~a:"
set "dir_path=%%~b\"
)
set dir_path=%dir_path:\=\\%
setlocal enableDelayedExpansion
for /f "useback skip=1 tokens=1,2* delims=:" %%f in (`" wmic datafile where (drive='!dir_drive!' and path like '%dir_path%') get CreationDate^,name"`) do (
set creation_date=%%f
set creation_date=!creation_date:~0,8!
set "file_name=%dir_drive%%%~g"
if 1!creation_date! GTR 1%compDate% (
echo !file_name!>>%exit_file%
)
)
exit /b 0

bat file read in file

i have a text file datefile.txt that contains
10-06-2013
and I tried to read it using the following bat file:
#echo off
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ datefile.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!"
echo(!var!
ENDLOCAL
)
echo %var%
the output I got are these:
10/06/2013
1:10/06/2013
how come my %var% is different from above one.
Or how could I remove "1:" in the %var%?
thanks.
You got this type of output, as the first line is written by echo(!var!.
The second line by echo %var%, but in the second case the variable doesn't contain the same.
This is because the Setlocal/endlocal block inside the for loop.
In your case you can simply remove the block, as your date doesn't contains any exclamation marks nor carets.
#echo off
SETLOCAL EnableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ datefile.txt"`) do (
set "var=%%a"
set "var=!var:*:=!"
echo(!var!
)
echo %var%
And if your file contains only one line, the solution could be simplified by
<datefile.tx set /p var=
echo %var%
the solution is to remove the "1:" using dos substr replacement:
http://www.dostips.com/DtTipsStringOperations.php#Snippets.Remove

Open one of a series of files using a batch file

I have up to 4 files based on this structure (note the prefixes are dates)
0830filename.txt
0907filename.txt
0914filename.txt
0921filename.txt
I want to open the the most recent one (0921filename.txt). how can i do this in a batch file?
Thanks.
This method uses the actual file modification date, to figure out which one is the latest file:
#echo off
for /F %%i in ('dir /B /O:-D *.txt') do (
call :open "%%i"
exit /B 0
)
:open
start "dummy" "%~1"
exit /B 0
This method, however, chooses the last file in alphabetic order (or the first one, in reverse-alphabetic order), so if the filenames are consistent - it will work:
#echo off
for /F %%i in ('dir /B *.txt^|sort /R') do (
call :open "%%i"
exit /B 0
)
:open
start "dummy" "%~1"
exit /B 0
You actually have to choose which method is better for you.
Sorry, for spamming this question, but I just really feel like posting The Real Answer.
If you want your BATCH script to parse and compare the dates in filenames, then you can use something like this:
#echo off
rem Enter the ending of the filenames.
rem Basically, you must specify everything that comes after the date.
set fn_end=filename.txt
rem Do not touch anything bellow this line.
set max_month=00
set max_day=00
for /F %%i in ('dir /B *%fn_end%') do call :check "%%i"
call :open %max_month% %max_day%
exit /B 0
:check
set name=%~1
set date=%name:~0,4%
set month=%date:~0,2%
set day=%date:~2,2%
if /I %month% GTR %max_month% (
set max_month=%month%
set max_day=%day%
) else if /I %month% EQU %max_month% (
set max_month=%month%
if /I %day% GTR %max_day% (
set max_day=%day%
)
)
exit /B 0
:open
set date=%~1
set month=%~2
set name=%date%%month%%fn_end%
start "dummy" "%name%"
exit /B 0
One liner, using EXIT trick:
FOR /F %%I IN ('DIR *.TXT /B /O:-D') DO NOTEPAD %%I & EXIT
EDIT:
#pam: you're right, I was assuming that the files were in date order, but you can change the command to:
FOR /F %%I IN ('DIR *.TXT /B /O:-N') DO NOTEPAD %%I & EXIT
then you have the file list sorted by name in reverse order.
Here you go... (hope no-one beat me to it...) (You'll need to save the file as lasttext.bat or something)
This will open up / run the oldest .txt file
dir *.txt /b /od > systext.bak
FOR /F %%i in (systext.bak) do set sysRunCommand=%%i
call %sysRunCommand%
del systext.bak /Y
Probably XP only. BEHOLD The mighty power of DOS.
Although this takes the latest filename by date - NOT by filename..
If you want to get the latest filename, change /od to /on .
If you want to sort on something else, add a "sort" command to the second line.
Use regular expression to parse the relevant integer out and compare them.