Script to compare file size of one file - powershell

I am trying to write a script that tell me if a file file size is greater than a number, below is the script I have written, but it keeps showing 1999 even though I know the file size is greater than the number.
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in (' powershell -c "(Get-ChildItem -Recurse "C:\XSTOREDB\xstore_data.mdf" | Measure-Object -Property Length -Sum).Sum" ') do set "foldsize=%%a"
if "!foldsize!" GTR "6000000000" (
exit /B 0
) else (
exit /B 1999
)
endlocal

I'm pretty sure you have 2 problems. The first problem is that you are doing an alphabetical comparison. Thus, "7" would be considered GTR "6000000000".
Your second problem is that numeric operations in cmd are limited to 32 bit integers. The maximum number you can use is 2^31 - 1, which is 2147483647, which is already less than the number you are interested in.
The typical workaround here is to either compare the "important" parts of the string, or truncate the N least significant digits.
Here is a quick example. It may contain issues/errors in implementation. I did echo exit /b so the script would run and you can see the results instead of actual exit /b.
#echo off
setlocal enabledelayedexpansion
rem first we will inspect the 10th and 11th digits from the right
set foldsize=7000000010
rem If there is any number in the 11th spot, then it must be greater
if not "!foldsize:~0,-10!"=="" (
rem the number is at least 10000000000 which is greater so we exit
echo exit /B 0
)
rem check if the 10th digit is 6 or greater
rem add a zero to avoid issues with an undefined variable
set smallfoldsize=!foldsize:~0,-9!
if !smallfoldsize!0 GTR 50 (
rem technically the number is now greater or equal to 6000000000
rem I will leave it up to you if you really need it to be greater than
echo exit /B 0
) else (
rem it is smaller
echo exit /B 1999
)
rem an alternative is to discard the N least significant digits
rem I am stripping the last 7 digits and then adding a zero to avoid
rem an undefined variable
set smallfoldsize=!foldsize:~0,-7!0
if !smallfoldsize! GEQ 6000 (
echo exit /B 0
) else (
echo exit /B 1999
)
rem again, this is actual greater or equal. If you
rem need greater than, you have to look at the less
rem significant digits when smallfoldsize = 6000

avery_larry's helpful answer does a great job of explaining the fundamental problems with your approach (lexical rather than numerical comparison, numbers that are too large).
If your comparison doesn't need to be accurate down to the byte level, you can try the following approach, which enables a simpler solution:
Make PowerShell return a value in mebibytes (MiB) rather than bytes.
This should return a small enough number that you can compare (numerically) with cmd's GTR operator.
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('
powershell -noprofile -c ^
"[math]::Ceiling((Get-ChildItem -File -Recurse \"C:\XSTOREDB\xstore_data.mdf\" | Measure-Object -Property Length -Sum).Sum / 1mb)"
') do set "foldsize=%%a"
:: # 6000000000 bytes is ca. 5722 MB
if !foldsize! GTR 5722 (
exit /B 0
) else (
exit /B 1999
)
Note how the PowerShell command divides the result by 1mb (1 MiB (mebibyte) = 2 ^ 20 = 1,048,576 bytes) and then rounds it up via [math]::Ceiling()
Also note how the embedded " chars. were escaped as \" to ensure that PowerShell sees them.
Finally, note how, as a small optimization, -File was passed to the Get-ChildItem cmdlet in order to skip directories (themselves, not their files), given that directories themselves have no Length property.

Despite your question body, text, and title, not matching the code you have supplied, I do not see any need for a for-loop for this task.
Based upon your question body text, and title, to exit with an errorlevel of 0 if file C:\XSTOREDB\xstore_data.mdf is greater than 6GB:
#"%__AppDir__%WindowsPowerShell\v1.0\powershell.exe" -NoP "If(Test-Path -L "C:\XSTOREDB\xstore_data.mdf"){(GI -L "C:\XSTOREDB\xstore_data.mdf").Length -GT 6GB}" 2>NUL|"%__AppDir__%find.exe" "T">NUL&&(Exit /B 0)||Exit /B 1999
Based upon your question code, to exit with an errorlevel of 0 if directory C:\XSTOREDB totals more than 6GB:
#"%__AppDir__%WindowsPowerShell\v1.0\powershell.exe" -NoP "If(Test-Path -L "C:\XSTOREDB"){(GCI "C:\XSTOREDB" -Rec|Measure Length -Su -EA Stop).Sum -GT 6GB}" 2>NUL|"%__AppDir__%find.exe" "T">NUL&&(Exit /B 0)||Exit /B 1999

Related

Using %date% in batch script

I want to have some code like this:
if %date% equ "Mon" echo do this do that
but the cmd window closes after encountering this code, even if I put
pause
after it.
How do I fix this?
Here's a complete cmd file that will give you what you need. The important bit is all in the getDow function and, hopefully, it's commented well enough to understand. First, the test harness:
#echo off
rem Test harness bit - just get current date and compare with getDow.
date /t
call :getDow num long short
echo Day of week is %num%, %long%, %short%
goto :eof
The function itself is:
rem Usage: call :getDow <num> <long> <short>
rem <num> will receive the numeric form (0-6).
rem <long> will receive the long form (e.g., Monday).
rem <short> will receive the short form (e.g., Mon).
:getDow
rem Create local scope to prevent information leakage.
setlocal enableextensions enabledelayedexpansion
rem Create array for translation.
set idx=0
for %%a in (Sunday Monday Tuesday Wednesday Thursday Friday Saturday) do (
set dow[!idx!]=%%a
set /a "idx += 1"
)
rem Get the numeric day of week, mmi command will
rem output 'DayOfWeek=N' and we just extract N.
for /f "tokens=2 delims==" %%a in ('WMIC Path Win32_LocalTime Get DayOfWeek /value ^| findstr "DayOfWeek="') do (
set localNum=%%a
)
set localStr=!dow[%localNum%]!
rem Properly end scope but let selected information leak.
endlocal&&set %1=%localNum%&&set %2=%localStr%&&set %3=%localStr:~0,3%
goto :eof
A sample run of that script gives:
Tue Jun 05
Day of week is 2, Tuesday, Tue
You probably want to use:
IF /I "%DATE:~,3%"=="Mon" (Echo Do this
Echo Do that)
Or possibly:
IF NOT "%DATE:Mon=%"=="%DATE%" (Echo Do this
Echo Do that)
However neither of those are safe or robust methods in anything other than your specific current user environment.
This is how I'd get the day of the week into a variable using a batch file with WMIC:
For /F %%A In ('WMIC Path Win32_LocalTime Get DayOfWeek') Do For %%B In (
Monday.1 Tuesday.2 Wednesday.3 Thursday.4 Friday.5 Saturday.6 Sunday.0
) Do If "%%~xB"==".%%A" Set "WDName=%%~nB"
Line 2 can be optionally adjusted to start with Sunday.0 Monday.1 etc. if necessary or Lunes.1 Martes.2 etc. depending upon your language.
You could then use:
If "%WDName%"=="Monday" (Echo Do this
Echo Do that)
Although (Get-Date).DayOfWeek in PowerShell seems so much simpler.

Batch Script to check first 5 char vs MM-DD

I'm trying to build a script for one of our remote media players and am trying to get it to update once a file shows up in the dropbox. I need it to check the first 5 of the title against MM-DD and if they match then play the video in question. Playing the video is no issue, neither are the archives. My issue right now is that when I try to make a for loop for the files in the location I'm getting the syntax of the command is incorrect or "x" was not expected at this time. also, my date is being formatted like this: 05 -02, and I dont know why.
:::::::::::::::::::::::::::::::::::::::::::::: Get Date :::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Use WMIC to retrieve date and time
FOR /F "skip=1 tokens=1-6" %%G IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') DO (
IF "%%~L"=="" goto s_done
Set _mm=00%%J
Set _dd=00%%G
)
:s_done
:: Pad digits with leading zeros
Set _mm=%_mm:~-2%
Set _dd=%_dd:~-2%
::Finalize Date
Set date=%_mm%-%_dd%
echo %date%
::::::::::::::::::::::::::: Check the downloads folder for completed video files :::::::::::::::::::::::::::::::
:: Loop through all files
:loop
for %%a IN ("dir \Dropbox\Trailer") DO (
::IF the name is not blank, get the first 5 characters of the video name
set first5=%a:~0,5%
::Compare those characters to the date
IF "%first5%" == "%date%" (
taskkill /im wmplayer.exe /t
::::::::::::::: Archive all previous Videos :::::::::::::
for /r %%i in ("dir \Dropbox\Trailer") do (
xcopy /s (%%i) "dir \Archived_Shows\"
)
ping localhost
"C:\Program Files\Windows Media Player\wmplayer.exe" "C:\Dropbox\Trailer\%%a" /fullscreen
:::::::::::::::::::::::::::::::: Exit if new video is running ::::::::::::::::::::::::::::::::::::::
exit
)
)
goto :loop
I'm not sure exactly what your script is supposed to be doing but here is an example based on my understanding.
#Echo Off
CD "Dropbox\Trailer" 2>Nul || Exit /B
For /F "Tokens=1-2" %%A In (
'WMIC Path Win32_LocalTime Get Day^,Month^|FindStr [1-9]'
) Do Set /A _dd=10%%A,_MM=10%%B
Set "DString=%_MM:~-2%-%_dd:~-2%"
:Loop
If Not Exist "%DString%*" (
Timeout 300 /NoBreak
GoTo Loop
)
TaskKill /IM WMPlayer.exe /T 2>Nul
Timeout 10 /NoBreak >Nul
XCopy "." "%~dp0Archived_Shows" /S /D /I
For %%A In ("%DString%*") Do (
"%ProgramFiles%\Windows Media Player\wmplayer.exe" "%%A" /FullScreen
)
GoTo Loop
If no matching file is found it currently checks for new ones every 300 seconds, (5 minutes). If a matching file is found, the loop only resumes once you close WMPlayer. You can change that timespan on line 11 as per your preference.
You are setting the variable %date%, which is a system dynamic variable. Attempting to modify the contents is an unrecommended act, so instead, use another name.
Also, I have simplified the get-and-trim MM-DD part, and it works properly for me.
for /F "skip=1 tokens=1-6" %%G IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') DO (
IF "%%~L"=="" goto s_done
Set _mm=00%%~J
Set _dd=00%%~G
)
:s_done
::Finalize Date
Set titleDate=%_mm:~-2%-%_dd:~-2%
echo %titleDate%
pause

batch file to open txt replace text save and close

I understand that this community is for helping people and I'm not one hundred percent how to ask for someone to do a commissioned job or w/e but I'd like a series of batch files for changing certain numbers to a particular number per batch file.
echo off
if exist %application.yml del %application.yml
for /F "delims=" %%l in (%1) do (
set "line=%%l"
set "line=%line:specific text=another word%"
echo/%line%>> %application.yml)'
How would I set this to replace an array of numbers to the set number, I'm sorry I'm a complete idiot with this.
File itself application.yml
product:
# Cntrl alt 1 Mini, 2 m16, 3 scar, 4 m4169, 5 akm, 6 ump,7 sks
discordUsername:
discordPassword:
maxCPUsUsage: 4
shutdownKey: 0x7B
reloadConfigKey: 0x79
screenResolutionX: 1920
screenResolutionY: 1080
playerEsp: true
playerEspColor: 0xFF00FF
playerEspPointSize: 1
radarEsp: true
radarEspColor: 0x0000FF
radarEspPointSize: 2
vehicleEsp: true
vehicleEspColor: 0xff0000
vehicleEspPointSize: 1
vehicleEspHotkey: 0x2D
lootEsp: true
lootEspColor: 0x00FF00
lootAirdropEspColor: 0xffffff
lootEspPointSize: 1
lootEspHotkey: 0x24
redrawTime: 0
reloadDataTime: 2000
aimEnabled: true
aimKey: 0x39
aimFOV: 95
aimFrequency: 6
aimPrecision: 1
aimSpeed: 1
bulletSpeed: 990
aimHitbox: 0
# Key list: https://msdn.microsoft.com/en-us/en-en/library/windows/desktop/dd375731(v=vs.85).aspx
So essentially the number I wish to replace is the bullet speed and it could be multiple numbers from previous changes ect, for example
990
870
400
and if it is any of these instances I wish to replace it with
900
and so on and so forth.
So it searches for any of the possible numbers and changes it to the specified number and save the file under the same file name.
Thanks in advance for any help if I'm being completely honest in the example I dont even know what to change to make it simply replace one number to the other never mind multiple instance searches :)
Once again ty for any advice or help.
The following commented .bat script should do the job, although empty lines are not copied to output file:
#ECHO OFF
SETLOCAL EnableExtensions
rem redirect all output to a brand new file
>application.yml (
rem parse a file (supplied file name %1 must not be equal to "application.yml")
for /F "usebackq delims=" %%l in ("%~1") do (
rem parse each line
for /F "tokens=1* delims=:" %%L in ("%%~l") do (
if /I "%%~L"=="bulletSpeed" (
rem modified line
echo(%%L: 900
) else (
rem original line
echo(%%l
)
)
)
)
BTW, in the original script, you would need Delayed Expansion to make proper reference to variables (re)defined within a parenthesised code block (like a FOR loop body), e.g. as follows:
echo off
SETLOCAL EnableExtensions EnableDelayedExpansion
if exist application.yml del application.yml
for /F "delims=" %%l in (%1) do (
set "line=%%l"
set "line=!line:specific text=another word!"
echo/!line!>>application.yml
)
Resources (required reading):
(command reference) An A-Z Index of the Windows CMD command line
(helpful particularities) Windows CMD Shell Command Line Syntax
(%~L etc. special page) Command Line arguments (Parameters)
(special page) EnableDelayedExpansion
(>>, > etc. special page) Redirection
Regular Expressions are ideal to do this sort of editing.
Aside from findstr's limited capabilities batch is lacking RE support.
This wrapped powershell one liner will do the job either on cmd line or in a batch-file.
powershell -NoP -C "(gc application.yml) -Replace '(?<=bulletSpeed: )\d+','900'|sc application.yml"
It uses a lookbehind assertion to replace any number following bulletSpeed: with 900.
To limit the replacement to one of previous 990, 870, 400 use this:
powershell -NoP -C "(gc application.yml) -Replace '(?<=bulletSpeed: )(990|870|400)','900'|sc application.yml"

Syntax of the "for" command: whats wrong with line 10 in following batch file?

1 echo off
2
3 echo Please enter a date
4 set /p a=
5 echo %a%
6 for /D %%d in (M:\Serienbriefauftrag\*) do (
7 if not exist %%d\Erledigt\*.bat echo %%d
8 )
9 pause
10 for /d %%b in (dir M:\Serienbriefauftrag\%%d /T:C)
11 echo %%b
12 pause
The Code works fine until line 10 (syntax error) the aim is, to get the creation date of the folder compare it with "%a%" and and if it's under the entered date "%a%", the folder should be moved. but somehow...
found a way to get the creation date
if not exist %%d\Erledigt\*.bat echo %%~td|findstr /i /l
the new task is to make the output "calculateable"
Two things:
1) In line 10 %%d is undefined (empty), since the scope of the first loop (where it is defined) is left in line 8.
You can set another variable to %%d to be able to use it after the loop ends, but be careful. In batch using set in for loops is a bit tricky. To get around the aweful use of delayed variable expansion, I would suggest to you staying inside of the loop while doing your work.
#echo off
set /p a=Please enter a date:
for /d %%d in (M:\Serienbriefauftrag\*) do (
if not exist %%d\Erledigt\*.bat (
:: Do whatever you want with %%d in here
echo "%%d"
)
)
pause
2) The syntax of your second loop is wrong. You are missing the do before the loop body and /d lets the loop iterate over all folders in a specified directory. dir M:\Serienbriefauftrag\%%d /T:C is not a directory but a command.
If you want to iterate over the output of that command, you have to use /f and put the contents of the paranthesis in single quotes, like that ('dir M:\Serienbriefauftrag\%%d /T:C').

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.