Abnormal behavior of batch script in If else condition - windows

As a beginner in batch file programming, i have created a batch file. Below is the code snippet-
SET INDEX=1
SET CURRJOBS=10
REM TOTALJOBS and CURRJOBS are dynamic but to keep code here, i have put static values to them
SET TOTALJOBS=1000
IF [%CURRJOBS%] LSS [%TOTALJOBS%] (
IF [%INDEX%] GEQ [5] (
SET /A INDEX=0
)
ECHO Started at %date% %time% with %CURRJOBS% jobs>>%CURRDIR%\JobSubmit.log
REM Here is a call to another bat file with Index.
ECHO Finished at %date% %time% with %CURRJOBS% jobs>>%CURRDIR%\JobSubmit.log
SET /A INDEX+=1
GOTO START
)ELSE (
ECHO Finished at %date% %time% with %CURRJOBS% jobs>>%CURRDIR%\JobSubmit.log
)
Now, this code, sometimes work, sometimes not.
however there is some syntax error which might be a cause to behave abnormally. Is there any IDE or online utility to check the syntax of batch file?
What is wrong with above code?

Comparisons in IF command are of two types: string or number. To indicate IF that we want number comparison, the numbers must be written with no additional characters. So, your code should be written this way:
IF %CURRJOBS% LSS %TOTALJOBS% (
IF %INDEX% GEQ 5 (
SET /A INDEX=0
)
When a variable or parameter may have an empty value, it is customary to enclose it between quotes to avoid syntax errors, for example:
IF "%POSSIBLEEMPTYVAR%" NEQ "" (
If the variable have string values, you may use the same format for both check for empty value and do the comparison:
IF "%VARIABLE%" equ "THIS VALUE" GOTO OK
However, if a variable may be empty and you want to compare it as number, both tests must be made.

Related

Safe number comparison in Windows batch file

I know that when comparing stuff for equality in a batch file it's common to enclose both sides in quotes, like
IF "%myvar% NEQ "0"
But when comparing using "greater than" or "less than", this doesn't work because the operands would then be treated as strings with quotes around them. So you can instead just do
IF %myvar% GTR 20000
The caveat is that if the variable %myvar% isn't declared, it would be like doing
IF GTR 20000
which is a syntax error.
I came up with the following workaround:
IF 1%myvar% GTR 120000
which I'm hoping would result in IF 1 GTR 120000 if myvar is undefined, and it seems to work.
Is this a safe way to compare numbers and accounting for undeclared variables, or did I just open up a whole new can of caveats?
Let us assume the batch file contains:
#echo off
:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range 0 to 20000.
set /P "MyVar=Enter number [0,20000]: "
As I explained by my answer on How to stop Windows command interpreter from quitting batch file execution on an incorrect user input? the user has the freedom to enter really anything including a string which could easily result in breaking batch file execution because of a syntax error or resulting in doing something the batch file is not written for.
1. User entered nothing
If the user hits just key RETURN or ENTER, the environment variable MyVar is not modified at all by command SET. It is easy to verify in this case with environment variable MyVar explicitly undefined before prompting the user if the user entered a string at all with:
if not defined MyVar goto PromptUser
Note: It is possible to use something different than set "MyVar=" like set "MyVar=1000" to define a default value which can be even output on prompt giving the user the possibility to just hit RETURN or ENTER to use the default value.
2. User entered a string with one or more "
The user could enter a string with one or more " intentionally or by mistake. For example pressing on a German keyboard key 2 on non-numeric keyboard with CapsLock currently enabled results in entering ", except German (IBM) is used on which CapsLock is by software only active for the letters. So if the user hits 2 and RETURN quickly or without looking on screen as many people do on typing on keyboard, a double quote character instead of 2 was entered by mistake by the user.
On MyVar holding a string with one or more " all %MyVar% or "%MyVar%" environment variable references are problematic because of %MyVar% is replaced by Windows command processor by user input string with one or more " which nearly always results in a syntax error or the batch file does something it was not designed for. See also How does the Windows Command Interpreter (CMD.EXE) parse scripts?
There are two solutions:
Enable delayed expansion and reference the environment variable using !MyVar! or "!MyVar!" as now the user input string does not affect anymore the command line executed by cmd.exe after parsing it.
Remove all " from user input string if this string should never contain a double quote character.
Character " is definitely invalid in a string which should be a number in range 0 to 20000 (decimal). For that reason two more lines can be used to prevent wrong processing of user input string caused by ".
set "MyVar=%MyVar:"=%"
if not defined MyVar goto PromptUser
The Windows command processor removes all doubles quotes already on parsing this line before replacing %MyVar:"=% with the resulting string. Therefore the finally executed command line set "MyVar=whatever was entered by the user" is safe on execution.
The example above with a by mistake entered " instead of 2 results in execution of set "MyVar=" which undefines the environment variable MyVar which is the reason why the IF condition as used before must be used again before further processing of the user input.
3. User entered non-valid character(s)
The user should enter a positive decimal number in range 0 to 20000. So any other character than 0123456789 in user input string is definitely invalid. Checking for any invalid character can be done for example with:
for /F delims^=0123456789^ eol^= %%I in ("%MyVar%") do goto PromptUser
The command FOR does not execute goto PromptUser if the entire string consists of just digits. In all other cases including a string starting with ; after zero or more digits results in execution of goto PromptUser because of input string contains a non-digit character.
4. User entered number with leading 0
Windows command processor interprets numbers with a leading 0 as octal numbers. But the number should be interpreted as decimal number even on user input it with one or more 0 at beginning. For that reason the leading zero(s) should be removed before further processing variable value.
for /F "tokens=* delims=0" %%I in ("%MyVar%") do set "MyVar=%%I"
if not defined MyVar set "MyVar=0"
FOR removes all 0 at beginning of string assigned to MyVar and assigns to loop variable I the remaining string which is assigned next to environment variable MyVar.
FOR runs in this case set "MyVar=%%I" even on user entered 0 or 000 with the result of executing set "MyVar=" which undefines environment variable MyVar in this special case. But 0 is a valid number and therefore the IF condition is necessary to redefine MyVar with string value 0 on user entered number 0 with one or more zeros.
5. User entered too large number
Now it is safe to use the command IF with operator GTR to validate if the user entered a too large number.
if %MyVar% GTR 20000 goto PromptUser
This last verification works even on user entering 82378488758723872198735897 which is larger than maximum positive 32 bit integer value 2147483647 because of the range overflow results in using 2147483647 on execution of this IF condition. See my answer on weird results with IF for details.
6. Possible solution 1
An entire batch file for safe evaluation of user input number in range 0 to 20000 for only decimal numbers is:
#echo off
set "MinValue=0"
set "MaxValue=20000"
:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
set /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "
if not defined MyVar goto PromptUser
set "MyVar=%MyVar:"=%"
if not defined MyVar goto PromptUser
for /F delims^=0123456789^ eol^= %%I in ("%MyVar%") do goto PromptUser
for /F "tokens=* delims=0" %%I in ("%MyVar%") do set "MyVar=%%I"
if not defined MyVar set "MyVar=0"
if %MyVar% GTR %MaxValue% goto PromptUser
rem if %MyVar% LSS %MinValue% goto PromptUser
rem Output value of environment variable MyVar for visual verification.
set MyVar
pause
This solution gives the batch file writer also the possibility to output an error message informing the user why the input string was not accepted by the batch file.
The last IF condition with operator LSS is not needed if MinValue has value 0 which is the reason why it is commented out with command REM for this use case.
7. Possible solution 2
Here is one more safe solution which has the disadvantage that the user cannot enter a decimal number with one or more leading 0 being nevertheless interpreted decimal as expected usually by users.
#echo off
set "MinValue=0"
set "MaxValue=20000"
:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
set /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "
if not defined MyVar goto PromptUser
setlocal EnableDelayedExpansion
set /A "Number=MyVar" 2>nul
if not "!Number!" == "!MyVar!" endlocal & goto PromptUser
endlocal
if %MyVar% GTR %MaxValue% goto PromptUser
if %MyVar% LSS %MinValue% goto PromptUser
rem Output value of environment variable MyVar for visual verification.
set MyVar
pause
This solution uses delayed environment variable expansion as written as first option on point 2 above.
An arithmetic expression is used to convert the user input string to a signed 32 bit integer interpreting the string as decimal, octal or hexadecimal number and back to a string assigned to environment variable Number on which decimal numeral system is used by Windows command processor. An error output on evaluation of the arithmetic expression because of an invalid user string is redirected to device NUL to suppress it.
Next is verified with using delayed expansion if the number string created by the arithmetic expression is not identical to the string entered by the user. This IF condition is true on invalid user input including number having leading zeros interpreted octal by cmd.exe or a number entered hexadecimal like 0x14 or 0xe3.
On passing the string comparison it is safe to compare value of MyVar with 20000 and 0 using the operators GTR and LSS.
Please read this answer for details about the commands SETLOCAL and ENDLOCAL because there is much more done on running setlocal EnableDelayedExpansion and endlocal than just enabling and disabling delayed environment variable expansion.
8. Possible solution 3
There is one more solution using less command lines if the value 0 is out of valid range, i.e. the number to enter by the user must be greater 0.
#echo off
set "MinValue=1"
set "MaxValue=20000"
:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
set /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "
set /A MyVar+=0
if %MyVar% GTR %MaxValue% goto PromptUser
if %MyVar% LSS %MinValue% goto PromptUser
rem Output value of environment variable MyVar for visual verification.
set MyVar
pause
This code uses set /A MyVar+=0 to convert the user entered string to a 32-bit signed integer value and back to a string as suggested by aschipfl in his comment above.
The value of MyVar is 0 after command line with the arithmetic expression if the user did not input any string at all. It is also 0 if the user input string has as first character not one of these characters -+0123456789 like " or / or (.
A user input string starting with a digit, or - or + and next character is a digit, is converted to an integer value and back to a string value. The entered string can be a decimal number or an octal number or a hexadecimal number. Please take a look on my answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files which explains in detail how Windows command processor converts a string to an integer value.
The disadvantage of this code is that a by mistake input string like 7"( instead of 728 caused by holding Shift on pressing the keys 2 and ( on a German keyboard is not detected by this code. MyVar has value 7 on user enters by mistake 7"(. Windows command processor interprets just the characters up to first not valid character for a decimal, hexadecimal or octal number as integer value and ignores the rest of the string.
The batch file using this code is safe against an unwanted exit of batch file processing because of a syntax error never occurs independent on what the user inputs. But a by mistake wrong input number is in some cases not detected by the code resulting in processing the batch file further with a number which the user did not want to use.
Answering the call to nitpick
Mofi has been requesting I write my own solution here, that is "shorter" as I pointed out to him the way he wrote his code using & instead of ( followed by a command then a carriage return and another command, or `( followed by a carriage return, followed by another command followed by a carriage return followed by another command) sets a precedent which makes this a hard task to agree on.
I also did not think this was the POINT of providing the answers perse, I mean I used to, but when changes are minor, and mainly fixing logic, or offering a minorly different solution, is that really a big difference? Does that really warrant being a separate answer?
That said, I don't see a better way without editing his response.. but this still leaves unresolved questions on what is being judged shorter.
Unfortunately as well, in discussing with Mofi he has edited his answer to one that can result in invalid choices.
While I have pointed this out, and I'm sure this was just a minor oversite on his part, I feel like not posting the code here has contributed to him actively deteriorating the quality of his question, which is always a possible outcome when nitpicking.
while Mofi was the driving force in that activity, I don't like the effect it's had on him as I was trying to avoid exactly this effect on my code by not getting into it, so I have decided to post the code comparison to bring some closure for them.
Please not, I will post his original code (the most recent one that did not use the erroneous method), and then refactored to how I would write it, and I will post my Original code, and then refactored to how I believe he would write it (may not be in that order but I will call out each)
So below is the result
Mofi Original:
This is hard to say if you should count every line, there are some instances where & is used to queue up commands and the IFS never use Parenthesis which I wouldn't generally do.
#echo off
set "MinValue=0"
set "MaxValue=20000"
:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
set /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "
if not defined MyVar goto PromptUser
setlocal EnableDelayedExpansion
set /A "Number=MyVar" 2>nul
if not "!Number!" == "!MyVar!" endlocal & goto PromptUser
endlocal
if %MyVar% GTR %MaxValue% goto PromptUser
if %MyVar% LSS %MinValue% goto PromptUser
rem Output value of environment variable MyVar for visual verification.
set MyVar
pause
My Code Refactored to Mofi's Form
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET /A "_Min=-1","_Max=20000"
:Menu
CLS
SET "_Input="
REM Prompt user for a positive number in range %_Min% to %_Max%.
SET /P "_Input=Enter number [%_Min%,%_Max%]: "
SET /A "_Tmp=%_input%" && if /I "!_input!" EQU "!_Tmp!" if !_Input! GEQ %_Min% if !_Input! LEQ %_Max% SET _Input & pause & GOTO :EOF
GOTO :Menu
Mofi's Code Refactored
Mofi's above code Refactored to my more compacted form Where ( have the first command follow except when used on an IF statement, and ) follow the last command. This also makes the entire portion that really does the validation EASY to discern, it is only the portion within the :PromtUser function, not counting REM lines or blank lines this is 13 lines of code.
#(SETLOCAL
echo off
SET /A "MinValue=0","MaxValue=20000")
CALL :Main
( ENDLOCAL
EXIT /B )
:Main
CALL :PromptUser MyVar
REM Output value of environment variable MyVar for visual verIFication.
SET MyVar
PAUSE
GOTO :EOF
:PromptUser
SET "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
SET /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "
IF NOT DEFINED MyVar GOTO :PromptUser
Setlocal EnableDelayedExpansion
SET /A "Number=MyVar" 2>nul
IF not "!Number!" == "!MyVar!" (
Endlocal
GOTO :PromptUser )
Endlocal
IF %MyVar% GTR %MaxValue% (
GOTO :PromptUser )
IF %MyVar% LSS %MinValue% (
GOTO :PromptUser )
GOTO :EOF
My Code in My Compact Form
To compare here is my code also in the same compact form I refactored Mofi's code to above. Again, only the lines inside of the function itself are "doing the heavy lifting" here and need compare. I did forget that when I worked on my code originally I was trying to match Mofi's form, and it allowed me an extra nicety in keeping my && ( in the following line or all as a single line. So I will post two varients
#(SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET /A "_Min=-1","_Max=20000" )
CALL :Main
( ENDLOCAL
EXIT /B )
:Main
CALL :Menu _input
REM Output value of environment variable _input for visual verIFication.
SET _input
PAUSE
GOTO :EOF
:Menu
CLS
SET "_input="
REM Prompt user for a positive number in range %_Min% to %_Max%. Store it in "_input"
SET /P "_Input=Enter number [%_Min%,%_Max%]: "
SET /A "_Tmp=%_input%" && (
IF /I "!_input!" EQU "!_Tmp!" IF !_Input! GEQ %_Min% IF !_Input! LEQ %_Max% GOTO :EOF )
GOTO :Menu
My Code in My Compact Form 2
#(SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET /A "_Min=-1","_Max=20000" )
CALL :Main
( ENDLOCAL
EXIT /B )
:Main
CALL :Menu
REM Output value of environment variable _input for visual verification.
SET _input
PAUSE
GOTO :EOF
:Menu
CLS
SET "_input="
REM Prompt user for a positive number in range %_Min% to %_Max%. Store it in "_input"
SET /P "_Input=Enter number [%_Min%,%_Max%]: "
SET /A "_Tmp=%_input%" || GOTO :Menu
IF /I "!_input!" EQU "!_Tmp!" (
IF !_Input! GEQ %_Min% (
IF !_Input! LEQ %_Max% (
GOTO :EOF ) ) )
GOTO :Menu

Windows Batch File Multiple labels back to back

In PHP or Javascript and other languages as well, there is the switch, case statement as a control flow tool. One of the neat features of that is it allows for multiple cases to be pointed to a single command group. For example:
switch(abc) {
case a:
case b:
case false:
console.log("hi")
break;
case c:
console.log("see ya")
break;
etc...
}
So that if abc is equal to a or b or is false, the "Hi" will be logged. Depending on the code, it can be a lot cleaner than calling from an object or tons of if else or if x || y || z statements.
I have a windows batch file where I'm doing the following:
GOTO %1
..... stuff
.... more stuff
REM =================LABELS BELOW==============
:-h
:--help
:-?
:--?
type help.txt
exit /b
It's more detailed than the above pseudocode, but that's the gist of it. It allows for aliases for the same argument And it works. If I execute mycmd -h or mycmd --help etc., I help the help file text displayed on the screen.
However, on the last line of my output, I get the error, THE SYSTEM CANNOT FIND THE BATCH LABEL SPECIFIED -.
The error might be caused by something else. I have some CALL commands and GOTO :EOF statements, so that certainly could be the source of the error.
But I've never seen the logic I applied above before used in a batch file, and I'm wondering if there are some other side effects that I might not be considering. Is it possible that I will encounter unpredictable side effects down the road? Is this bad practice for any reason?
Update:
I hadn't posted my code, because I think it's hard to read, and it's a work in progress. Basically, what you're seeing here is argument parsing. I'm parsing the passed values in to categories - imagine this example: node --file=d.js. I'm calling the --file the param, and the d.js the arg. And much of the code below is creating an array of each.
#echo off
SETLOCAL enabledelayedexpansion
#SET "T=%1"
IF /i NOT DEFINED T (GOTO -h)
SET /a counter=1
SET /a argCount=0
SET /a index=0
for %%x in (%*) do set /A argCount+=1
for /l %%x in (1,2,%argCount%) do (
call SET param[!index!]=%%!counter!
set /a counter+=2
SET /a index+=1
)
SET /A paramCount=!index!
SET /a counter=2
SET /a index=0
for /l %%x in (2,2,%argCount%) do (
call SET arg[!index!]="%%!counter!"
set /a counter+=2
SET /a index+=1
)
for /l %%i in (0,1,!paramCount!) do (
SET "arg=!ARG[%%i]!"
SET "p=!param[%%i]!"
CALL :!p! !arg!
)
GOTO END
:-h
:--help
:--?
:-?
type %~dp0\lib\help.txt
GOTO END
:--unzip
SET "a=%1"
IF /i NOT DEFINED a (
SET "MASK=\d+-[a-z]+.zip"
) ELSE if [%a%]==["all"] (
SET "MASK=\d+-[a-z]+.zip"
) else (
SET "MASK=!a!"
)
for /f "usebackq tokens=*" %%i in (`dir /a /b %dls%^|grep -iE "!MASK!"`) do (
#7z x %dls%\%%i -omypath\share\icons
)
GOTO :EOF
:END
#echo Done
ENDLOCAL
Update: I don't know if this will be helpful for anyone, but the problem was with SET /A paramCount=!index!. That let to always looking for a parameter with an argument no matter what, so that if my second parameter didn't have an argument, or neither did, it was causing problems. I 'solved' the problem by setting paramCount to !index!-1, but I think the upshot of this is that it's quite difficult to pass arbitrary, potentially optional, parameters to batch files, and probably should be parsed differently than I have done - or using a different coding language. That said, it's working fine now for what I need.

Why does my batch file fail to get file size?

I'm trying to write a Windows batch file that uses ffmpeg to convert whole folders with old *.flv videos into *.mp4 videos.
The batch file more or less works, but I want to do some test before deleting the source file. One of these test is that the output file should be at least 2/3 of the original file, but I can't get it to work.
Here's my bat file (with all the debugging echo lines included):
#echo off
setlocal EnableExtensions EnableDelayedExpansion
::-------------------------------------------------------------------------------------
:: get options and folder path
set opzione=%~1%
set cartella=%~2%
:: who's who?
if "%opzione:~3,1%"=="" (
echo.
) else (
if "%opzione:~0,1%"=="/" (
echo.
) else (
set opzione=%~2%
set cartella=%~1%
)
)
::echo.
::echo Cartella = %cartella%
::echo Opzione = %opzione%
::echo.
::-------------------------------------------------------------------------------------
:Check_path
set FLV_FOLDER="%cartella%"
if %FLV_FOLDER% == "" (
echo ... Invalid
goto :uscita
) else (
echo ... OK.
)
::-------------------------------------------------------------------------------------
:Check_Options (STILL W.I.P.)
set Lista=0
set Convert=0
set Delete=0
if "%opzione%"=="/c" (set Convert=1)
if "%opzione%"=="/l" (set Lista=1)
if "%opzione%"=="/d" (set Delete=1)
::echo Lista = %Lista%
::-------------------------------------------------------------------------------------
:Loop_path
#cls
echo Looping all .flv files in %FLV_FOLDER%...
for /R %FLV_FOLDER% %%a IN (*.flv) do call :Converting_Function "%%a"
goto :uscita
::-------------------------------------------------------------------------------------
:Converting_Function
set infile="%~1"
set outfile="%~dpn1.mp4"
set outsize=0
set insize=0
set minsize=0
if not %Lista%==0 goto :just_list
echo Converting %infile% to %outfile%
ffmpeg -v error -i %infile% -c copy -copyts %outfile%
::....................CHECKS........................................................
echo Errors from ffmpeg?
if errorlevel 1 goto :error_ffmpeg
echo Do the outfile exist?
if not exist %outfile% goto :error_exist
echo Is outfile big enough?
:: (say yes if outfile size > infile size*2/3)
for /f %%S in (%outfile%) do set "outsize=%%~zS"
echo %outfile% size is %outsize%
for /f %%S in (%infile%) do set insize=%%~zS
echo %infile% size is %insize%
set /A "minsize=(%insize%*3)/2"
echo minsize is %minsize%
if not %outsize% GTR %minsize% goto :error_size
ren "%~1" "%~n1.todelete"
:: del /q %infile%
goto :eof
:error_ffmpeg
echo Convertion error
pause
if exist %outfile% del /q %outfile%
goto :eof
:error_exist
echo %outfile% does not exist
pause
goto :eof
:error_size
echo Size of %outfile% is 0
pause
goto :eof
:just_list
echo %infile%
goto :eof
:uscita
pause
This is the output:
Converting "T:\C++Stuffs\_PROVACONV_\Monaco - Machine_#1 - Monday Morning.flv" to "T:\C++Stuffs\_PROVACONV_\Monaco - Machine_#1 - Monday Morning.mp4"
[flv # 0000000000577320] Packet mismatch 107347968 1638 1638
Errors from ffmpeg?
Do the outfile exist?
Is outfile big enough?
"T:\C++Stuffs\_PROVACONV_\Monaco - Machine_#1 - Monday Morning.mp4" size is
"T:\C++Stuffs\_PROVACONV_\Monaco - Machine_#1 - Monday Morning.flv" size is
Operando mancante.
minsize is 0
0 non atteso.
D:\ffmpeg-20170204-b1e2192-win64-static\bin>
Operando mancante means Missing Operand, 0 non atteso means Unexpected 0
Why do I not have the file size in the variables? What is the missing operand?
The environment variables infile and outfile are defined with file name being enclosed in double quotes. That is not recommended as explained in answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? But it is valid and works here as expected.
The command line to get file size of output file
for /f %%S in (%outfile%) do set "outsize=%%~zS"
is processed before execution by Windows command interpreter for example to
for /f %S in ("C:\Path\File Name.mp4") do set "outsize=%~zS"
It can be read on executing in a command prompt window for /? that for /F interprets the set (string between round brackets) as string to process if enclosed in double quotes except the option usebackq is used which is not done here. For that reason FOR splits up the string C:\Path\File Name.mp4 into tokens using space/tab as delimiters and assigns the first token to loop variable S. So assigned to S for the example is C:\Path\File. The file size for this file can't be determined by Windows command interpreter as this file does not exist.
The solution is using FOR without option /F:
for %%S in (%outfile%) do set "outsize=%%~zS"
And the command line to get file size of input file
for /f %%S in (%infile%) do set insize=%%~zS
can be replaced by
set "insize=%~z1"
The help output on running in a command prompt window call /? explains this argument modifier for getting the size of a file passed as first argument to the batch file on calling it.
The help output on running set /? explains that in arithmetic expressions the current values of environment variables can be referenced by specifying the environment variables with just their names without using % or !. This works even within a command block beginning with ( and ending with matching ).
The command line with the arithmetic expression
set /A "minsize=(%insize%*3)/2"
can result on insize not being defined in execution of
set /A "minsize=(*3)/2"
This explains the error message because there is indeed missing the left operand for the multiplication.
The solution is using the arithmetic expression as recommended by help of command SET.
set /A "minsize=(insize*3)/2"
This arithmetic expression never fails on evaluation. In case of environment variable insize is not defined, it is replaced by 0 on evaluation of the arithmetic expression as explained by the help.
See also Debugging a batch file.
And please note that Windows command interpreter supports only arithmetic expressions with 32-bit signed integer values. So video files with a file size of 2 GiB or more cannot be correct processed by your batch code.

Windows Batch IF !hour! EQU "09" not returning TRUE

I have an application that updates a .txt file every 10 minutes. Once a day at the first time the file is updated after 0900 (9am) I want to send an email of that file. The updated file (pointed to by the SET command on line 3) can have a timestamp of anytime between 0900 and 0910.
What I’m proposing to do is run a batch file at 0857 every day that runs for 15 minutes checking the date stamp of the file until the hour becomes 09, then it sends the email and finishes.
In the code extract below to test the function, I’m having a problems with the simple compare statement:
IF !hour! EQU "09" (GOTO :rundailymail) ELSE (Timeout /T 6).
Even though (according to echo when I run it), hour is “09”, the compare returns false.
To test it you need to have a file with a timestamp of between 0900 and 0959.
I’m struggling, and have tried lots of things to get this working (and I’ve left some of the diagnostics in there). Any help or advice much appreciated.
#echo on
Setlocal EnableDelayedExpansion
SET filename="D:\Temp Files\test.txt"
IF NOT EXIST %filename% GOTO log
rem echo %filedatetime%
for /l %%x in (1, 1, 20) do (
FOR %%f IN (%filename%) DO SET filedatetime=%%~tf
echo !filedatetime!
SET hour= "!filedatetime:~11,-6!"
Echo !hour!
IF !hour! EQU "09" (GOTO :rundailymail) ELSE (Timeout /T 60)
)
:failedtofindfile
ECHO "Failed to find the right file timestamp"
goto end
:rundailymail
ECHO "send the daily email"
goto end
:log
ECHO "FILE MISSING"
goto end
:end
The batch code without assigning strings with the double quotes to environment variables and enclose instead the variable references where needed in double quotes:
#echo off
setlocal EnableDelayedExpansion
set "FileName=D:\Temp Files\test.txt"
if not exist "%FileName%" goto Log
for /L %%X in (1,1,20) do (
for %%F in ("%FileName%") do set "FileDateTime=%%~tF"
echo !FileDateTime!
set "Hour=!FileDateTime:~11,2!"
echo !Hour!
if "!Hour!" == "09" (
goto RunDailyMail
) else (
timeout /T 60
)
)
:FailedToFindFile
echo Failed to find the right file timestamp
goto End
:RunDailyMail
echo Send the daily email
goto End
:Log
echo FILE MISSING: %FileName%
goto End
:End
endlocal
The original code failed because of the space character after equal sign on line
SET hour= "!filedatetime:~11,-6!"
which is also assigned to the variable hour like the double quotes.
For that reason the compared strings are  "09" and "09" and the strings are not equal as it can be seen here now.
If the string comparison would be without usage of delayed expansion, the comparison would by chance work because string value of hour would be with the surrounding double quotes and the leading space on standard expansion would be just an additional space between command IF and the double quoted first string resulting in ignoring this extra space. But delayed expansion is required here and therefore the space is not ignored on the string comparison.
Set the answers on
How to set environment variables with spaces?
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
why it is nearly always better to define variables with syntax set "variable=string value" and use double quotes around the variable references where needed.
BTW: The usage of CamelCase spelling for variable names and labels make them easier readable.

Batch script: local variable from function1 to function2

Okay guys, let my try to explain my problem:
I start with a line from where I start 2 different functions
setlocal EnableDelayedExpansion
for %%i in ("C:\*.*") do (
call :function1 "%%~i"
call :function2 "%%~i"
)
goto :eof
In function1, at a certain point I DO a SET in a local environment:
setlocal EnableDelayedExpansion
...
...
set name1=blabla
endlocal & SET name=%name1%
echo %name%
goto :eof
The echo does return my variable. Now onto my problem.
I quit function one and i go to function 2 (see first code-segment)
I can't call the variable form here. I tried in the function2, I tried before function2 is called, but both didn't resolve the issue.
My guess is a have set only a local variable for function1. I search the nets but i read that the line "endlocal & SET name=%name1%" should have solved my issue.
I hope I have explained it well, all help appreciated!
I'm not sure where your problem lies since this works perfectly:
#setlocal enableextensions enabledelayedexpansion
#echo off
set name=ORIGNAME
for %%i in (1 2) do (
call :function1 %%i
echo in main %name% !name!
)
endlocal
goto :eof
:function1:
setlocal enableextensions enabledelayedexpansion
set name1=%1_blabla
endlocal & SET name=%name1%
echo in function %name%
goto :eof
outputting:
in function 1_blabla
in main ORIGNAME 1_blabla
in function 2_blabla
in main ORIGNAME 2_blabla
Are you certain that, when you used name in the main areas, you used !name! instead of %name%?
If you used the %name% variant, that would be evaluated when the entire for loop was read, not at the time when you used it (in other words, it would be blank). You can see that in the output of ORIGNAME in the main line.
I'm not certain that's the case since you are using delayed expansion. But, just in case, I thought I'd mention it. I always use delayed expansion and I always used the ! variants of the environment variables since it more closely matches how I expect a shell to work.
In any case, the code I've given works fine so you may want to fiddle with that to see if you can incorporate it into your own.
First a small working sample
#echo off
setlocal EnableDelayedExpansion
call :myAdd returnVar 1 2
echo 1. Percent %returnVar%
echo 1. Exlcam !returnVar!
(
call :myAdd returnVar 4 5
echo 2. Percent %returnVar%
echo 2. Exlcam !returnVar!
)
goto :eof
:myAdd
setlocal
set /a result=%2 + %3
(
endlocal
set %1=%result%
goto :eof
)
---- Output ----
1. Percent 3
1. Exlcam 3
2. Percent 3
2. Exlcam 9
The cause for the wrong result in "2. Percent" is a result of the expanding of %var%.
They are expanded at the time of parsing, before executing any line of the parenthesis block, so there is the old value in returnVar.
But that is also the cause why the returning of variables from a function works.
the endlocal removes all local variables, but the block (an ampersand works the same way)
is expanded before the "endlocal" is executed.
It's a good idea to test this issues with "echo on".

Resources