Can you see any reason why this would display 0
"%ConnectDirectExe%" -f"%ConnectDirectCredentials%" < "%CurrentCDConfigFile%" > "%CurrentCDTaskLogFile%"
ECHO %errorlevel%
yet this displays blank?
"%ConnectDirectExe%" -f"%ConnectDirectCredentials%" < "%CurrentCDConfigFile%" > "%CurrentCDTaskLogFile%"
SET Result=%errorlevel%
ECHO errorlevel is %Result%
Note that the "%ConnectDirectExe%" line in both cases exactly matches. If I invert the blocks to run in the opposite order, the results are similarly inverted, too.
Why can't I store the result of %errorlevel% in Result and display it? It keeps on appearing as blank.
Update:
it seems like my SET statements aren't being executed if they occur within the IF Block.
Given the following batch file, if.bat:
echo off
SET Result=1
ECHO ResultCaption1=%Result%
IF 1 equ 1 (
SET Result=2
ECHO ResultCaption2=%Result%
) else (
ECHO 1 is not = 2
)
When I run it, i see this output:
C:\Batch>if.bat
C:\Batch>echo off
ResultCaption1=1
ResultCaption2=1
C:\Batch>
Shouldn't the second ECHO display
ResultCaption2=2
?
I suspect your code is embedded within a parenthesised block, perhaps as part of an IF statement or a FOR loop. The problem with that is the expansion occurs when the line is parsed, and the entire block is parsed at once, before anything in the block is executed. So %errorlevel% will be the value before your command was executed, and %Result% will probably be undefined.
The solution is to use delayed expansion, which must be enabled before it can be used. Type HELP SET from the command prompt to get more info about delayed expansion and how it can help when dealing with blocks of code.
setlocal enableDelayedExpansion
for ... in(...) do (
"%ConnectDirectExe%" -f"%ConnectDirectCredentials%" < "%CurrentCDConfigFile%" > "%CurrentCDTaskLogFile%"
SET Result=!errorlevel!
ECHO errorlevel is !Result!
)
Related
relevant code looks like this:
cd /d %~dp0
if exist filename.txt (
echo %date% %time% *** text... >> filename2.txt
echo ==============================
echo === text....... ===
echo === text....... ===
echo === text....... (text...) ===
echo === text (text...
echo === text...).
:loop
set /p "varx= text: "
if "%varx%" neq "xxxx" goto loop
... more script...
)
Have searched high and low for solutions...
was pointed in direction of If statement groupings here:
https://www.petri.com/forums/forum/windows-scripting/general-scripting/57625-if-exists-file-was-unexpected-at-this-time - NO GO
was pointed in direction of problems with loops in If blocks, here:
(Windows batch) Goto within if block behaves very strangely - NO GO
Was pointed in direction of using #setlocal enabledelayedexpansion or #setlocal enableextensions (can't track down where) - NO GO
tried passing filename via set /p varfile="filename" and if exist %varfile% - NO GO
of course thought there were other parts of code causing error -- NO GO
The thing is that this WAS working for a long while...then I changed what I thought was some innocuous stuff and cannot figure out where the problem lies...
such an obscure problem to solve..ugh!
WELL...I am posting this as a Q & A...because I solved my own problem (took me a couple of days of trial and error to narrow down and fix the problem). I am hoping this will help some poor soul too and save them a lot of heartache, and a few hair strands...
The thing that got me thinking the most was the second bullet regarding poor handling of loops inside If statements...BUT that was not the real reason but something similar...
It turns out the problem was with the use of "(' and/or ')' on the ECHO lines...
I thought this was innocuous... I use brackets in ECHO lines in lots of places, and these lines were about 10-15 lines down from where the error message was being generated, so naturally I was not thinking that this was at all the source.
BUT it turns out that the interpreter clearly does not like the use of '(' or ')' even on ECHO lines if they are used within the If statement blocks. It must still treat them as special characters in that context...it clearly does not ignore them...
Solution:
Either taking the '(' and ')' out of those 3 lines above OR simply REM those lines out solved the problem and the error message went away...all is finally well...
BTW it is possible that the a similar thing may apply to FOR statements too (I vaguely recall reading something about FOR acting strangely too).
So food for thought.
The entire compound-statement from the if exist ... ( through to the closing parenthesis is a code block.
Within a code block, labels are not allowed, an un-escaped ) will terminate the block and any %var% is replaced by that variable's value when the block is encountered.
You need to call the :loop routine (easiest way)
if exist filename.txt (
echo %date% %time% *** text... >> filename2.txt
echo ==============================
echo === text....... ===
echo === text....... ===
echo === text....... (text...^) ===
echo === text (text...
echo === text...^).
call :loop
... more script...
)
... etc
goto :eof
:loop
set /p "varx= text: "
if "%varx%" neq "xxxx" goto loop
goto :eof
Note the goto :eof is defined to be "go to end-of-file" (The colon is required)
Note the call has a colon before the label - this indicates it's an internal routine - it resides in this batchfile
Note that each ) is now escaped by a ^ which makes ) an ordinary character, not a statement-terminator.
You've probably removed a redirector - any echo within the called procedure will be gathered into the redirected output - that is if you have
(
whatever...
)>filename
then any data echoed within the code block will be redirected to the file - including echoes within a called procedure. You can explicitly redirect an echo within such a block or procedure using echo to console>con for instance.
Encasing any sequence of lines in parentheses, making it a code block allows redirection of the echoed output from the block, so
(
echo this
echo that
)>filename
creates a new filename containing the sum of the echoes rather than having to append >>filename to each line. Obviously, >> in place of > will append to any existing file in the usual manner.
I am trying to write a batch file that does the following:
Prompt user for the directory to create the new folder newest
Prompt user for an integer limit
Create directory newest
CD newest
FOR loop for limit iterations
Create directory "Month " + iteration
For example:
newest = Reports
limit = 12
I should end up with:
\Reports\Month 1
\Reports\Month 2
\Reports\Month 3
...
\Reports\Month 12
This is my code so far:
setlocal enabledelayedexpansion
FOR /L %%i IN (1,1,%limit%) DO (
set "month_counter=Month %%i"
echo %month_counter%
MD %month_counter%
)
endlocal
If I set limit = 12, I get 12 error messages stating:
Echo is off.
The syntax of the command is incorrect.
I appreciate the help.
FOR /L %%i IN (1,1,%limit%) DO (
MD "Month %%i"
)
You have the standard delayed expansion problem - hundreds of articles on SO about this.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var. CALL ECHO %%errorlevel%% displays, but sadly then RESETS errorlevel.
So - you could use
set "month_counter=Month %%i"
CALL echo %%month_counter%%
If you really, really want to - or one of the other techniques, but it's far easier to simply make the directory from your constant data + the iteration counter in %%i as shown.
So i am attempting to make a simple script to check if an application is running using a external text file (using 1 and 0 for if running or is not). However i cannot seem to get the statement working correctly..
setlocal enabledelayedexpansion
set /p Running=<IsRunning.txt
IF %Running% EQU 0(GOTO ProgramNotRunning)
IF %Running% EQU 1(GOTO ProgramRunning)
:ProgramNotRunning
echo program starting
echo 0 >IsRunning.txt
echo 1 >IsRunning.txt
GOTO:EOF
:ProgramRunning
echo program already running
GOTO:EOF
The issue is no matter what value it is, it always only ever runs the ProgramNotRunning code block and not the other.
Prior to using EQU, i was simply using == to check for equivilance.
Much thanks for any assistance given!
1 - Missing spaces
If %Running% EQU 0 (...
^ This space is needed
2 - In batch files lines of code are executed one after the other unless one command changes this behaviour. You can iterate with for, jump with goto, call subroutines with call, leave the batch, the subroutine or the console with exit, ... but a label will not break the execution. After your if %Running% EQU 1 ... there isn't anything that prevent execution to continue into the code following code it none of the if tests find a coincidence. So, if the set /p does not retrieve 0 or 1 the code after :ProgramNotRunning will be always executed.
3 - Missing/empty file. If IsRunning.txt can not be found or it is empty (or at least the first line is empty) or if it does contain an unexpected value, the if lines will fail. The code executed is
file missing : if EQU 0 (
line/file empty : if EQU 0 (
bad data : if this is a test EQU 0 (
All this cases will cause the line to be considered an error and the execution will be canceled.
#echo off
setlocal enableextensions disabledelayedexpansion
rem Retrieve running state
set "Running="
if exist "IsRunning.txt" (
set /p "Running=" < "IsRunning.txt"
)
IF "%Running%" EQU "0" goto ProgramNotRunning
IF "%Running%" EQU "1" goto ProgramRunning
rem As there is no valid data, assume the program is not running
:ProgramNotRunning
echo program starting
>"IsRunning.txt" (echo 1)
goto :eof
:ProgramRunning
echo program already running
goto :eof
Why >"IsRunning.txt" (echo 1)? Just to be sure there are no aditional spaces after the 1 that will be included in the output (as happens with your code), retrieved when the line is readed from the file and causing the if to fail
if "1 " EQU "1" ( ... This will be evaluated to false
And this still leaves cases where the data retrieved can make the code fail. For a 0/1 test, it is easier to not read the file, just test for presence of the file. If the file exist, the program is running, else it is not running.
echo off
set /a a=0
:start
if %a% LEQ 21(
echo test
set /a a=%a%+1
goto start
)
I don't get it...
I only get a Syntax-error when the loop starts.
The code you posted isn't a valid bash script it's a batch script to echo test 20 times in a bash script using a for loop like this:
#/bin/bash
for i in {1..20}; do
echo test
done
Much clearer and readable than batch scripts, welcome to bash!
Besides the error already explained by Bali C, you should note that your code have a potential problem. In this command: set /a a=%a%+1 the %a% value is expanded just one time when the if command is executed. In this case your code run by chance because the goto command cause the if be executed again in each loop, but if your program would be larger and more complex, and this set command would be executed several times inside parentheses, the code would fail because %a% will be expanded just one time to the value that a variable had before enter the parentheses.
The way to solve this problem is easy in this case: just eliminate the percent signs, because set /a a=a+1 command can directly take the values of the variables. However, there is much more involved here! For further details, type set /? and pay attention to "delayed variable expansion" description.
You are missing a space between 21 and ( which will make it compare the number in the loop to 21(, also breaking the if statement, the latter being why you are getting a syntax error.
Add a space and it works fine.
echo off
set /a a=0
:start
if %a% LEQ 21 (
echo test
set /a a=%a%+1
goto start
)
My batch file terminates prematurely after I assign the first environmental variable (script output below). I've tried turning echo on, using errorlevels, sending the output to a text file, and checking syntax. I've spent several hours researching debugging batch scripts, but I have finally hit a brick wall.
Script's Goal: Search each directory name of the user's Program Files, looking for common antivirus programs. I realize that it would be easiest iterate through an array of antivirus names for this purpose, but I want to keep it simple for now.
#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 echo %ERRORLEVEL%
else echo "env. variable created successfully."
for /d %%f in (""%ProgramFiles%\*"") do (
{
IF NOT ERRORLEVEL 0 echo %ERRORLEVEL%
echo "%%f"
if exist /i "*McAfee*" < %%f %AntiVirus1%="McAfee"
::find "Norton" < %%f
::find "Comodo" < %%f
::find "AVG" < %%f
}
echo %AntiVirus1%
#pause
Output of this script:
C:\Users\Matt\Desktop>set AntiVirus1="Initial Value"
C:\Users\Matt\Desktop>
Can someone point me to what I'm doing wrong?
UPDATE Corrected script, now working but returning incorrect results:
::#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 (echo %ERRORLEVEL%) ELSE echo "env. variable created successfully."
echo Checking Program Files...
for /d %%f in ("%ProgramFiles%\*") do (
echo "%%f"
if %%f=="*adobe*" set AntiVirus1="adobe"
)
echo %AntiVirus1% found
#pause
First of all, ELSE must be on the same line with IF or on the same line with the closing parenthesis that pertains to IF. In you particular case you should change your first IF...ELSE command like this:
IF NOT ERRORLEVEL 0 (ECHO %ERRORLEVEL%) ELSE ECHO "env. variable created successfully."
or like this:
IF NOT ERRORLEVEL 0 (
ECHO %ERRORLEVEL%
) ELSE ECHO "env. variable created successfully."
(Capitalisation and indentation are perfectly optional.)
Other issues:
Duplicated quotation marks in the FOR loop header:
for /d %%f in (""%ProgramFiles%\*"") do (
should be
for /d %%f in ("%ProgramFiles%\*") do (
Braces ({, }) around the loop body. They are not part of the loop syntax (in fact, they are not part of batch scripting syntax at all), so should be dropped.
No closing parenthesis matching the opening one after DO. It should be added on a separate line after the loop body.
Incorrect use of ::-style comments in the loop body. They are not allowed inside bracketed blocks. Use REM instead.
UPDATE
In batch scripting, testing for a substring is done somewhat unusually. You'll need another environment variable and you'll also need to enable delayed expansion. The latter is not really connected with the comparison, but it is needed because the comparison is going to be performed within a bracketed block.
Here's your new script modified, with the changes highlighted:
::#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 (echo %ERRORLEVEL%) ELSE echo "env. variable created successfully."
SETLOCAL EnableDelayedExpansion
echo Checking Program Files...
for /d %%f in ("%ProgramFiles%\*") do (
echo "%%f"
SET "folder=%%f"
if /I NOT "!folder:adobe=!"=="!folder!" set AntiVirus1="adobe"
)
echo %AntiVirus1% found
#pause
Here's a bit of explanation.
The ! syntax is a delayed expansion equivalent of % and is used with environment variables only, not with loop variables and not with command line parameters. Delayed expansion is needed because we are in a bracketed block. A bracketed block is parsed entirely before it starts executing, so all %var% expressions are expanded (evaluated) before the block starts and are not changed throughout the block's execution. That cannot suit us because we need to assign different values to a variable during the block's execution, and the values must be read within the block. Delayed expansion, as follows from the name, delays the expansion of a variable until the actual execution of every single command that references that variable. Because immediate expansion can still be used alongside delayed expansion, a different syntax is introduced, which is ! around variable names, instead of %.
!folder:adobe=! means evaluate folder replacing every occurrence of adobe with an empty string. The result of this expression is then compared to the (unchanged) value of folder. If there's a match, then the replacement didn't occur, which means there was no adobe in the value of folder in the first place. In this case we should do nothing. But if there was not a match, i.e. if the modified value didn't match the unmodified one, then we should set the AntiVirus1 variable. This is why there's NOT in front of the comparison.
The /I option simply means case-insensitive comparison.