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.
Related
How to stop overwriting but add a new number beside the name.
I tried to make this code, but it didn't work.
set overwrite=0
set newoverwrite=0
:loop
if (%textfile.txt%==EXIST) goto fix
:fix
set /a newoverwrite=%overwrite%+1
if (%textfile%newoverwrite%.txt%==EXIST) goto fix
echo hello > textfile%newoverwrite%.txt
echo skipped!
pause > nul
It is a bit sloppy because I have tested it many times.
Does anyone know how? Or fix my code?
I am assuming you want to create files by just incrementing numbers to it.
#echo off & set cnt=0
:loop
type "textfile%cnt%.txt">nul 2>&1 && echo skipped textfile%cnt%.txt || (echo hello)>"textfile%cnt%.txt"
set /a cnt+=1
pause
goto :loop
I am not sure though if you are aware that this will create files in a permanent loop until you interrupt the process. so Do Not remove the pause from the script.
To explain what it does. We test the file for existance by attemtping to type the file. If the file does exist, type is successful and generates errorlevel of 0. This is where the && conditional operators come into play. If the errorlevel is 0 then it will execute the command after && which is to echo Skipped... However, should the file not exist will it will generate errorlevel of 1 and skip the && operator where the code after || will be executed.
A frequent method to handling errors within Windows batch scripts is to use things like
if errorlevel 1 ... or if %errorlevel% neq 0 .... Often times one wants the error handling code to preserve the ERRORLEVEL.
I believe all external commands will always result in ERRORLEVEL being set to some value, so the error handling code must preserve the ERRORLEVEL in an environment variable prior to executing an external command.
But what about internal commands? The problem is, some internal commands clear the ERRORLEVEL to 0 when they succeed, and some do not. And I can't find any documentation specifying which commands do what.
So the question is, which internal commands clear the ERRORLEVEL to 0 upon success? This is not a general question about returned ERRORLEVEL codes, but strictly about success results.
There are posts like What is the easiest way to reset ERRORLEVEL to zero? and Windows batch files: .bat vs .cmd? that give partial answers. But I have never seen a comprehensive list.
Note: I've been curious about this for years. So I finally decided to run a bunch of experiments and come up with a definitive answer. I'm posting this Q&A to share what I have found.
This answer is based on experiments I ran under Windows 10. I doubt there are differences with earlier Windows versions that use cmd.exe, but it is possible.
Also note - This answer does not attempt to document the ERRORLEVEL result when an internal command encounters an error (except for a wee bit concerning DEL and ERASE)
Not only are there difference between commands, but a single command can behave differently depending on whether it was run from the command line, or within a batch script with a .bat extension, or from within a batch script with a .cmd extension.
The following set of commands never clear the ERRORLEVEL to 0 upon success, regardless of context, but instead preserve the prior ERRORLEVEL:
BREAK
CLS
ECHO
ENDLOCAL
FOR : Obviously, commands in the DO clause may set the ERRORLEVEL, but a successful FOR with at least one iteration does not set the ERRORLEVEL to 0 on its own.
GOTO
IF : Obviously, commands executed by IF may set the ERRORLEVEL, but a successful IF does not set ERRORLEVEL to 0 on its own.
KEYS
PAUSE
POPD
RD
REM
RMDIR
SHIFT
START
TITLE
The next set of commands always clear the ERRORLEVEL to 0 upon success, regardless of context:
CD
CHDIR
COLOR
COPY
DATE
DEL : Always clears ERRORLEVEL, even if the DEL fails (except when run without any file argument).
DIR
ERASE : Always clears ERRORLEVEL, even if ERASE fails. (except when run without any file argument).
MD
MKDIR
MKLINK
MOVE
PUSHD
REN
RENAME
SETLOCAL
TIME
TYPE
VER
VERIFY
VOL
Then there are these commands that do not clear ERRORLEVEL upon success if issued from the command line or within a script with a .bat extension, but do clear the ERRORLEVEL to 0 if issued from a script with a .cmd extension. See https://stackoverflow.com/a/148991/1012053 and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J for more info.
ASSOC
DPATH
FTYPE
PATH
PROMPT
SET
Lastly, there are these commands that do not fit neatly into any of the prior categories:
CALL : If a :routine or batch script is CALLed, then ERRORLEVEL is exclusively controlled by the CALLed script or :routine. But any other type of successful CALL to a command will always clear ERRORLEVEL to 0 if the CALLed command does not otherwise set it.
Example: call echo OK.
EXIT : If used without /B, then the cmd.exe session terminates and there is no more ERRORLEVEL, just the cmd.exe return code. Obviously EXIT /B 0 clears the ERRORLEVEL to 0, but EXIT /B without a value preserves the prior ERRORLEVEL.
I believe that accounts for all internal commands, unless there is an undocumented command that I missed.
Your description of CALL command is incomplete:
CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it.
Example: call echo OK.
Check this small example:
#echo off
call :setTwo
echo Set two: %errorlevel%
call :preserve
echo Preserve: %errorlevel%
call echo Reset
echo Reset: %errorlevel%
call :subNotExists 2> NUL
echo Sub not exist: %errorlevel%
goto :EOF
:setTwo
exit /B 2
:preserve
echo Preserve
exit /B
Output:
Set two: 2
Preserve
Preserve: 2
Reset
Reset: 0
Sub not exist: 1
CALL description should say something like this:
CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it. Example: call echo OK, but if the called command is a subroutine it preserves the prior ERRORLEVEL. If the called subroutine does not exist, it sets the ERRORLEVEL to 1.
Trying my hands on windows batch files, in the below code that I found by searching in www.
#ECHO OFF
REM Call this with two arguments, and it will add them.
SET a=%1+%2
IF %ERRORLEVEL%==0 (goto errors-0) ELSE (goto errors-1)
REM Instead of using goto with the variable, this uses an IF-ELSE structure
:errors-0
REM This is if it was successful
ECHO %a%
goto exit
:errors-1
REM this is if it had an error:
ECHO Errors occurred.
goto exit
REM GUESS WHAT, THIS REM WILL NEVER EVER BE READ! IT WILL BE SKIPPED OVER BY THE GOTOS
:exit
ECHO.
ECHO press any key to exit.
PAUSE>nul
The code is suppose to take 2 arguments, add them and echo the result.
But this won't execute with success on my Windows 8.1 machine. Below is what I get:
C:\ProjectDoc>add3.bat
Errors occurred.
press any key to exit.
So, U added an echo for the ERRORLEVEL to see its value after executing the command SET. Below is the output:
C:\ProjectDoc>add3.bat 2 3
9009
Errors occurred.
press any key to exit.
C:\ProjectDoc>
So, is this errorlevel in Windows equal to the $? of Linux. Should it be returning 0 for every successful execution of a command or is it different? Reading some docs relates it to the Linux $? but it isn't clearly working as $? in Linux.
Yes, to be precise, %errorlevel% is analogous to $? in Bash shell.
In your batch file, SET a=%1+%2 is not doing what you expect it to do. It just sets the value of the variable a to the string "2+3" assuming you ran the file with arguments 2 3. If you want to do arithmetic you need to use the /A option: set /a a=%1+%2.
The SET command (and many other built-in commands) only set the ERRORLEVEL if there has actually been an error. If it was successful, the ERRORLEVEL will retain its previous value. I think this is what you're witnessing in your question.
By contrast, when a command runs an executable file, when the process completes it always sets the ERRORLEVEL.
As well as checking the ERRORLEVEL variable for specific values, it is idiomatic (for historical reasons) to check the errorlevel using the following expression
IF ERRORLEVEL 1 ECHO Hello
This will run the given command if ERRORLEVEL is 1 or above - in other words, if any error has occurred.
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!
)
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.