My colleague and I have been pulling our hair out all day over this.
We have a simple Windows batch file. We want it to read from a text file whose file path we are generating programmatically, take the single numeric value in this file, and compare it to a local variable. But we're getting completely inexplicable behavior.
The file contains a single scalar number, such as the number 2. Here's the code:
ThisAppFlagFileName=foo.txt
if not exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO do something here
) else (
SET /P InstalledVersion=<"%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%"
ECHO We think the file contains: %InstalledVersion%
IF %InstalledVersion% GEQ %ThisVersionInstallDataNum% (
ECHO Version %ThisVersion% of the %ThisAppVisibleName% has already been installed for this user; exiting.
GOTO TheEnd
)
)
:TheEnd
Echo END
We keep getting an error reading 2 was unexpected at this time. So we inserted some trace message and, just in case the else was problematic, stuck to two different if statements:
ThisAppFlagFileName=foo.txt
if not exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO do something here
)
ECHO Trace Message 1 before IF
if exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO Trace Message 2 after IF before CD
SET /P InstalledVersion=<%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%"
ECHO We think the file contains: %InstalledVersion%
IF %InstalledVersion2% GEQ %ThisVersionInstallDataNum% (
ECHO Version %ThisVersion% of the %ThisAppVisibleName% has already been installed for this user; exiting.
GOTO TheEnd
)
)
:TheEnd
Echo END
And we see only the first trace message (before the if statement), and not the second trace message. So our conclusion is that somehow the content of the file is being interpolated into the line if exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%", but of course we don't understand why the first if not exist works but the second doesn't.
Can anyone spot the mistake, please? Environment is Windows 7 cmd.exe window, but we are hoping to deploy to both Windows 7 and Windows XP.
The issue here is that the entire IF expression is evaluated before the SET /P statement within it can be executed. InstalledVersion is not set yet, and so this invalid expression is evaluated:
IF GEQ 2 (
Nothing inside of the IF expression executes because it cannot be completely evaluated.
A solution is to enable delayed expansion and replace %InstalledVersion% with !InstalledVersion!, as described in this post.
You can also restructure the code so the GEQ comparison happens after the IF expression.
Your code have several errors. The first line:
ThisAppFlagFileName=foo.txt
missed a set command, so it is tryed to be executed as ThisAppFlagFileName command. This mean that ThisAppFlagFileName variable is NOT defined in your program, so InstalledVersion variable is never read from the file.
All references to InstalledVersion variable must use Delayed Expansion, that is, enclose they between exclamation marks instead percents and include setlocal EnableDelayedExpansion command at beginning of your program.
setlocal EnableDelayedExpansion
set ThisAppFlagFileName=foo.txt
if not exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO do something here
) else (
SET /P InstalledVersion=<"%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%"
ECHO We think the file contains: !InstalledVersion!
IF !InstalledVersion! GEQ %ThisVersionInstallDataNum% (
ECHO Version %ThisVersion% of the %ThisAppVisibleName% has already been installed for this user; exiting.
GOTO TheEnd
)
)
:TheEnd
Echo END
You must be aware that all variables that are modified inside parentheses must also be enclosed in exclamation marks instead percent signs. Search for "delayed expansion" for details.
Related
i am trying to run below snippet of code on my windows server.
#echo off
set BRANCH_NAME_ID=compiler_branch
if %BRANCH_NAME_ID%==compiler_branch ( echo INSIDE COMPILER BRANCH )
echo %BRANCH_SHORT_ID%|findstr /r "^[r][0-9][0-9]*_00$" & IF %ERRORLEVEL% == 0 ( echo IN IF ) ELSE ( echo INFO else)
pause
I was expecting the only output should be INSIDE COMPILER BRANCH because, BRANCH_NAME_ID variable is referring to compiler_branch. But some reason i am also getting IN IF as well.
Ouptut:-
INSIDE COMPILER BRANCH
IN IF
Press any key to continue . . .
As per the document https://ss64.com/nt/findstr.html i notice below and wrote the script accordingly. But some reason %ERRORLEVEL% is setting to 0 in line3 of my code thought the string is not matching :-
FINDSTR will set %ERRORLEVEL% as follows:
0 A match is found in at least one line of at least one file.
1 If a match is not found in any line of any file, (or if the file is not found at all).
2 Wrong syntax
An invalid switch will only print an error message in error stream.
Am i missing something ?
Because of how the interpreter reads files (see How does the Windows Command Interpreter (CMD.EXE) parse scripts? for a massive amount of info), %ERRORLEVEL% in that line gets replaced with its current value before the line is actually run. In order to have the command run and then have the value checked correctly, put the if statement on its own line.
#echo off
set BRANCH_NAME_ID=compiler_branch
if %BRANCH_NAME_ID%==compiler_branch ( echo INSIDE COMPILER BRANCH )
echo %BRANCH_SHORT_ID%|findstr /r "^[r][0-9][0-9]*_00$"
IF %ERRORLEVEL% == 0 ( echo IN IF ) ELSE ( echo INFO else)
pause
If for some reason you absolutely insist on using & to chain commands together (there is no reason to ever do this and it only makes things worse imo), then you can enable delayed expansion and use !ERRORLEVEL! instead.
#echo off
setlocal enabledelayedexpansion
set BRANCH_NAME_ID=compiler_branch
if %BRANCH_NAME_ID%==compiler_branch ( echo INSIDE COMPILER BRANCH )
echo %BRANCH_SHORT_ID%|findstr /r "^[r][0-9][0-9]*_00$" & IF !ERRORLEVEL! == 0 ( echo IN IF ) ELSE ( echo INFO else)
pause
I have a bundled if statement that checks whether java is installed and whether or not i'm at college or at home it is part of a larger piece of code but doesn't work on its own either any help would be much appreciated
Triple Checked i have the right number of brackets and equals signs and making sure no obvious mistakes were made so i'm confused as to what has gone wrong
if exist 'C:\Temporary Workspace' (set installtype=College)
where java >nul 2>nul
pause
if %errorlevel%==1 (
if %installtype%==College (
goto :college
) else (
set /P javaboolean=Java was not found is java installed? (Y/N)
if %javaboolean%==Y (
echo Please enter the path to java.exe
set /P javalocation=e.g. C:\Program Files\Common Files\Oracle\Java\javapath\java.exe
)
)
)
pause
exit
:college
echo success for college detection
pause
The error message and run time:
D:\Batch Testing>echo Checking Location...
Checking Location...
D:\Batch Testing>if exist 'C:\Temporary Workspace' (set installtype=College)
D:\Batch Testing>where java 1>nul 2>nul
D:\Batch Testing>pause
Press any key to continue . . .
( was unexpected at this time.
I expect the output to be 'success for college detection' as the directory does exist
you need to change your ifs a little bit
the first could be changed to
if exist "C:\Temporary Workspace" set installtype=College
note that the single apostrophe is not a valid quotation mark in windows cmd. And you can remove your parentheses because they are not needed.
the second could be written as
if errorlevel 1 (
because errorlevel is not %errorlevel% (for a detailed explanation, read https://devblogs.microsoft.com/oldnewthing/20080926-00/?p=20743 )
and the last could be
if .%installtype%==.College (
because when installtype is blank the if instruction makes no sense and produces syntax error; inserting the two . avoids it.
... and, as a bonus, you might want to consider to use if /i to ignore case when comparing, so College, college, COLLEGE or even cOllegE would match.
Well, there are several issues in your code. The line
if exist 'C:\Temporary Workspace' (set installtype=College)
will never set the variable installtype, because this checks whether 'C:\Temporary exists, which does not, since the apostrope ' is just an ordinary character for the Windows Command Prompt cmd. To make it work you must use quotation marks ":
if exist "C:\Temporary Workspace\" (set "installtype=College")
The trailing \ lets the condition check for existence of a directory called Temporary Workspace but not for a file with that name.
Note that I used the quoted set syntax here, which is generally recommended as it protects special characters.
The condition
if %installtype%==College (
will return a syntax error, because the variable installtype has not been set before (due to the wrong syntax you used). To avoid trouble with empty variables, quote both comparison expressions:
if "%installtype%"=="College" (
This is also described in this thread.
The command
set /P javaboolean=Java was not found is java installed? (Y/N)
will fail when in a parenthesised block of code, because the ) will unintentionally be recognised by cmd and close the block at an undesired position. Again use quotation to solve that:
set /P javaboolean="Java was not found is java installed? (Y/N)"
or:
set /P "javaboolean=Java was not found is java installed? (Y/N)"
However, the condition
if %javaboolean%==Y (
will still fail, because the queried variable javaboolean is set in the same block of code, so the returned value is the one present when the entire block is parsed, which is an empty string most probably. To overcome this, you must apply delayed variable expansion.
Put this somewhere before to enable delayed expansion:
setlocal EnableDelayedExpansion
Then replace the %-signs by exclamation marks ! (as you can see I again applied quotation here):
if "!javaboolean!"=="Y" (
Then put this somewhere after to end the environment localisation introduced by setlocal:
endlocal
Regard that all environment changes (variables, current directories) after setlocal are lost after endlocal and the state before setlocal becomes restored.
A similar problem is also described in this question and solved by this answer.
Instead of set /P just for a Yes/No decision consider to use the choice command, which does not allow to enter anything else:
rem /* The default choices are `Y` and `N`;
rem `choice` sets `ErrorLevel` to `1` when pressing `Y` and to `2` when pressing `N`: */
choice /M "Java was not found is java installed"
rem // The following means: if ErrorLevel >= 1 and if ErrorLevel < 2:
if ErrorLevel 1 if not ErrorLevel 2 (echo Yes has been chosen.)
Instead of
exit
you should use
exit /B
to only terminate the batch script but keep the hosting cmd instance alive. This is particularly helpful when you execute the batch file from a Command Prompt window rather than by double-clicking its icon in Explorer as you can view the console output then.
Instead of the string comparison
if %errorlevel%==1 (
you could also do true numeric comparison like
if %errorlevel% equ 1 (
or, if you are fine with the condition to be true if ErrorLevel is also greater than one,
if ErrorLevel 1 (
Alternatively, you could even use condition execution operators, which query the exit code:
where java > nul 2>&1 && (
rem // The code here is executed when `where` succeeds.
) || (
rem // The code here is executed when `where` fails.
)
So finally, here is the fixed script:
Code based on yours:
if exist "C:\Temporary Workspace\" (set "installtype=College")
where java > nul 2>&1
pause
if ErrorLevel 1 (
if "%installtype%"=="College" (
goto :college
) else (
set /P javaboolean="Java was not found is java installed? (Y/N) "
setlocal EnableDelayedExpansion
if "!javaboolean!"=="Y" (
endlocal
echo Please enter the path to java.exe
set /P javalocation="e.g. C:\Program Files\Common Files\Oracle\Java\javapath\java.exe "
) else endlocal
)
)
pause
exit /B
:college
echo success for college detection
pause
Variant using conditional execution (||) and the choice command:
if exist "C:\Temporary Workspace\" (set "installtype=College")
where java > nul 2>&1 || (
if "%installtype%"=="College" (
goto :college
) else (
choice /M "Java was not found is java installed"
if ErrorLevel 1 if not ErrorLevel 2 (
echo Please enter the path to java.exe
set /P javalocation="e.g. C:\Program Files\Common Files\Oracle\Java\javapath\java.exe "
)
)
)
pause
exit /B
:college
echo success for college detection
pause
I've read numerous threads on different approaches to getting the windows batch file parser to properly handle variables that have spaces, parentheses, and other special characters, but none of the recommendations seems to be able to fix the issue I am having.
Here is the script (prior to trying any workarounds), whose goal is to set a value for variable03 based on the values found for variable01 and variable02:
set variable01="C:\Program Files (x86)\SomeProgram\Subfolder"
set variable02="${macro}"
set variable01=%variable01:"=%
set variable02=%variable02:"=%
set variable03=""
if %variable02:~0,1%==$ (
if %variable01:~0,1%==$ (
set variable03=%variable03:"=%
) else (
set variable03=-o '%variable01%'
)
)
...the values of variable01 and variable02 are not known in advance - they are substituted by another program prior to running the script, so the above script is showing an example set of values for variable01 and variable02 after that substitution has been made.
The error I get when this script runs is:
\SomeProgram\Subfolder' was unexpected at this time.
...which corresponds to the last 'set' line in the above script. I assumed that this error was due to the parentheses in the value of variable01.
If I change that line to this:
set "variable03=-o '%variable01%'"
...then I get this error:
Files was unexpected at this time.
...which seems to indicate that it is trying to tokenize on the spaces in variable01, and the parser is still not happy.
If I then add this line at the top of the script:
setlocal enableextensions enableDelayedExpansion
...and change %variable01% to !variable01!, I still get the same error.
Clearly, I do not understand what the batch file parser needs to meet my requirement that the value of variable03 has the following value:
-o 'C:\Program Files (x86)\SomeProgram\Subfolder'
...any suggestions?
As Nate wrote, the problem in this case are the brackets, but the complete code is still unstable.
It's always better to use delayed expansion, as this is safe against any special characters.
And you should use the extended syntax of SET set "variable=content" to enclose the complete expression with quotes, then it's nearly safe, and the quotes are not added to the content.
And you don't need to remove the quotes later.
This should work with any content in var1 and var2
setlocal EnableDelayedExpansion
set "variable01=C:\Program Files (x86)\SomeProgram\Subfolder"
set "variable02=${macro}"
set "variable03="
if "!variable02:~0,1!"=="$" (
if "!variable01:~0,1!"=="$" (
rem
) else (
set "variable03=-o '!variable01!'"
)
)
echo(!variable03!
The problem is the parentheses in variable01's value. Since it's being expanded in an if condition, those parentheses are being interpreted as flow control. Fix by always putting it in double quotes.
set variable01="C:\Program Files (x86)\SomeProgram\Subfolder"
set variable02="${macro}"
set variable01=%variable01:"=%
set variable02=%variable02:"=%
set variable03=""
if "%variable02:~0,1%"=="$" (
if "%variable01:~0,1%"=="$" (
set variable03=%variable03:"=%
) else (
call :set3 "%variable01%"
)
)
goto :eof
REM If it is important to have single quotes instead of double then
REM I found I had to call a subroutine. Otherwise the set could be
REM left up in the else.
:set3
set variable03=-o '%~1'
goto :eof
I recently discovered a simply way to get user input in windows cmd.exe batch files using the /p option provided by set command. But for a strange reason that I don't understand the set /p behaves tricky when used between a if statement.
First, create a batch file named "ScriptRunsOK.bat" code below:
#echo off
rem Gets a User Yes-No Choice
set /p UserInput=Your Choice [Y/N]:
set UserChoice=%UserInput:~0,1%
if "%UserChoice%"=="y" set UserChoice=Y
if "%UserChoice%"=="Y" (
echo You Accepted[%UserChoice%] typing %UserChoice%
) else (
echo You Rejected[%UserChoice%] typing %UserChoice%
)
pause
Second, create a batch file "ScriptRunsBAD.bat" enclosing the "User Input & Evaluation" code in a intentionaly only for example purpouses) between a if statement. Code as follows:
#echo off
rem Gets a User Yes-No Choice. Choice Nested in a IF Statement
set DummyVar=OK
if "%DummyVar%"=="OK" (
set /p UserInput=Your Chiuce [Y/N]:
set UserChoice=%UserInput:~0,1%
if "%UserChoice%"=="y" set UserChoice=Y
if "%UserChoice%"=="Y" (
echo You Accepted[%UserChoice%] typing %UserChoice%
) else (
echo You Rejected[%UserChoice%] typing %UserChoice%
)
)
pause
Third, run "ScriptRunsOK.bat" just double-clicking on it, or directly from de command line repeatedly. It works fine every time you run it. But if you try the same with "ScriptRunsBAD.bat", it doesn't work as I espected and furthermore, when you run it from command line it, strangely, preserves the user input typed on the previous excecution.
What has the code for "ScriptRunsBAD.bat" that causes the behavior? Is there an additional consideration I must have when using the set /p command within a if or multi-line statement?
Well. Finally, after reading the content of the links posted by #melpomene and #Wimmel (thanks) I could solve the issue. In short words (further details follow the links posted) cmd scripting evals (by default) variable evaluation when every line is parsed (not the habitual behavior expected when you are a programmer). The script solved uses SETLOCAL ENABLEDELAYEDEXPANSION to enable "Delayed Expansion" that allows that variables be expanded at execution time rather than at parse time when referenced using exclamation points (see !UserInput:~0,1! and !UserChoice!).
The script solved:
#echo off
rem Gets a User Yes-No Choice. Choice Nested in a IF Statement
set DummyVar=OK
SETLOCAL ENABLEDELAYEDEXPANSION
if "%DummyVar%"=="OK" (
set /p UserInput=Your Choice [Y/N]:
set UserChoice=!UserInput:~0,1!
if "!UserChoice!"=="y" set UserChoice=Y
if "!UserChoice!"=="Y" (
echo You Accepted[!UserChoice!] typing !UserInput!
) else (
echo You Rejected[!UserChoice!] typing !UserInput!
)
)
ENDLOCAL
I am new here and my english is not very good so first, pardon me.
My problem is that this batch keep reading the strings inside the variable "IF EXIST %FSIZE%"; i meant, in case that variable does not exist, the batch keep reading inside the () brackets instead go on the rest of the stings.
If %FSIZE% exist, the batch perform the 2 task i assign: (1.) If size is equal, goto cifiles5x. (2.) If size is NOT equal it uses 7z to extract the file i want to be there.
If %FSIZE% DOES NOT EXIST, the batch keep saying "765952 was not expect at this moment".
I am taking the advices on ss64.com like don't use brackets or quotes when comparing numeric values (%size% EQU 765952) but i don't understand why it does not continue to where the ) ends.
I have also try to link the commands with "&&" so i can erase the brackets but the results are the same.
I know there's 2 spaced patches without quotes; they are unquoted because if i did the size checker won't work.
Thanks for reading this.
EDIT: Batch modified according to suggestions made.
#ECHO OFF
TITLE Log checker
COLOR 0F
SET FSIZE=%ProgramFiles(x86)%\Ces\Log Files Ver\LogVbReg_r2.dll
SET InsDIR=%ProgramFiles(x86)%\Ces\Log Files Ver\
REM I didn't add "" on FSIZE and InsDIR because if i did, quote the variable will
REM result a doubled quoted patch and won't work.
CLS
ECHO ==============================================================================
ECHO = Log checker =
ECHO ==============================================================================
ECHO Checking if exist:
ECHO "%FSIZE%"
IF EXIST "%FSIZE%" (
ECHO It does exist, checking size...
FOR %%A IN ("%FSIZE%") DO SET SIZE=%%~ZA
IF "%SIZE%" EQU "765952"
ECHO Size is right, jumping to CIFILES5
GOTO CIFILES5x
) ELSE (
ECHO Size is not right, extracting the needed file...
7z.exe e "Data_X.*" -o"%InsDIR%" -y "LogVbReg_r2.dll"
GOTO CIFILES5x)
ECHO Does not exist; extracting file...
REN Data_X.* Data_X.exe
Data_X.exe
TIMEOUT 2>NUL
REN DATA_X.* Data_X.dat
:CIFILES5x
ECHO Reach cifiles5x
PAUSE
IF EXIST "%TEMP%\9513.CES" (GOTO OPS) ELSE (GOTO LNKD)
You have space in your file name of %FSIZE%. As result text after space it is treated as command for if. Try using quotes around "%FSIZE%" or do CD to folder first and than check for just file name.
You have several minor errors:
If the file name may have spaces, it MUST be enclosed in quotes in all cases:
IF EXIST "%FSIZE%" (
FOR /F is used to read file CONTENTS. If you want to process file NAME, don't use /F option, but plain FOR command:
FOR %%A IN ("%FSIZE%") DO SET SIZE=%%~ZA
The ELSE part of an IF command must appear in a line alone:
GOTO CIFILES5x
) ELSE (
The same thing for a parentheses:
GOTO CIFILES5x
)
Excepting if the opening parentheses appear in the same line. This is correct:
IF EXIST "%TEMP%\9513.CES" (GOTO OPS) ELSE (GOTO LNKD)
If you compare numbers for equality (EQU, NEQ) then don't matter if they are enclosed in quotes or not. Just in case of GTR, GEQ, LSS and LEQ they must have NOT quotes. However, in your case, this is not a problem...