How to formulate a Batch script if else clause - batch-processing

I have a simple test to tell me if tomcat8 is running or not. It simply outputs if the service is found or not.
net start | find "Tomcat8" > nul 2>&1
if %ERRORLEVEL%==0 echo "Tomcat8 is running"
if %ERRORLEVEL%==1 echo "Tomcat8 not running"
How could I actually integrate this into a function so I could first test if tomcat8 is running, and if yes - goto end:, and if not - start the service?
eg.

Read if /?:
%ERRORLEVEL% will expand into a string representation of the current
value of ERRORLEVEL, provided that there is not already an
environment variable with the name ERRORLEVEL, in which case you
will get its value instead. After running a program, the following
illustrates ERRORLEVEL use:
goto answer%ERRORLEVEL%
:answer0
echo Program had return code 0
:answer1
echo Program had return code 1
Unfortunately, above code snippet seems to be a bit confusing as displays both messages in case of errorlevel 0. Try next (combined with find return codes):
net start | find "Tomcat8" > nul 2>&1
goto answer%ERRORLEVEL%
:answer0
echo `find` had return code 0, Tomcat8 is running
goto :acorp
:answer1
echo `find` had return code 1, Tomcat8 not running
rem code snippet to start the service here
goto :acorp
:answer2
rem dummy/debugging
echo `find` had return code 2: invalid switch is given
pause
:acorp
Another approach: IF… ELSE …
Parenthesis can be used to split commands across multiple lines. This
enables writing more complex IF… ELSE… commands:
IF EXIST filename.txt (
Echo deleting filename.txt
Del filename.txt
) ELSE (
Echo The file was not found.
)
When using parenthesis the CMD shell will expand [read] all the
variables at the beginning of the code block and use those values even
if the variables value has just been changed. Turning on
DelayedExpansion will force the shell to read variables at the
start of every line.
net start | find "Tomcat8" > nul 2>&1
if %ERRORLEVEL%==1 (
echo "Tomcat8 not running"
rem code snippet to start the service here
) else (
echo "Tomcat8 is running"
)

Related

How do I request and receive user input in a .bat and use it to run a certain program?

This is what I have so far
#echo off
:Ask
echo Would you like to use developer mode?(Y/N)
set INPUT=
set /P INPUT=Type input: %=%
If %INPUT%=="y" goto yes
If %INPUT%=="n" goto no
If %INPUT%=="Y" goto yes
If %INPUT%=="N" goto no
:yes
java -jar lib/RSBot-4030.jar -dev
echo Starting RSbot in developer mode
:no
java -jar lib/RSBot-4030.jar
echo Starting RSbot in regular mode
pause
Either way if the user enters y or n it always runs in -dev mode.
How do I make it run in -dev mode if the answer is yes, and regular mode if the answer is no. Also, how do I make it ask again if the input isn't Y, N, y, or n?
If the input is, say, N, your IF lines evaluate like this:
If N=="y" goto yes
If N=="n" goto no
…
That is, you are comparing N with "y", then "n" etc. including "N". You are never going to get a match unless the user somehow decides to input "N" or "y" (i.e. either of the four characters, but enclosed in double quotes).
So you need either to remove " from around y, n, Y and N or put them around %INPUT% in your conditional statements. I would recommend the latter, because that way you would be escaping at least some of the characters that have special meaning in batch scripts (if the user managed to type them in). So, this is what you should get:
If "%INPUT%"=="y" goto yes
If "%INPUT%"=="n" goto no
If "%INPUT%"=="Y" goto yes
If "%INPUT%"=="N" goto no
By the way, you could reduce the number of conditions by applying the /I switch to the IF statement, like this:
If /I "%INPUT%"=="y" goto yes
If /I "%INPUT%"=="n" goto no
The /I switch makes the comparisons case-insensitive, and so you don't need separate checks for different-case strings.
One other issue is that, after the development mode command is executed, there's no jumping over the other command, and so, if the user agrees to run Java in the development mode, he'll get it run both in the development mode and the non-development mode. So maybe you need to add something like this to your script:
...
:yes
java -jar lib/RSBot-4030.jar -dev
echo Starting RSbot in developer mode
goto cont
:no
java -jar lib/RSBot-4030.jar
echo Starting RSbot in regular mode
:cont
pause
Finally, to address the issue of processing incorrect input, you could simply add another (unconditional) goto command just after the conditional statements, just before the yes label, namely goto Ask, to return to the beginning of your script where the prompt is displayed and the input is requested, or you could also add another ECHO command before the jump, explaining that the input was incorrect, something like this:
#echo off
:Ask
echo Would you like to use developer mode?(Y/N)
set INPUT=
set /P INPUT=Type input: %=%
If /I "%INPUT%"=="y" goto yes
If /I "%INPUT%"=="n" goto no
echo Incorrect input & goto Ask
:yes
...
Note: Some of the issues mentioned here have also been addressed by #xmjx in their answer, which I fully acknowledge.
i just do :
set /p input= yes or no
if %input%==yes echo you clicked yes
if %input%==no echo you clicked no
pause
Here is a working example:
#echo off
:ask
#echo echo Would you like to use developer mode?(Y/N)
set INPUT=
set /P INPUT=Type input: %=%
If /I "%INPUT%"=="y" goto yes
If /I "%INPUT%"=="n" goto no
goto ask
:yes
#echo you select yes
goto exit
:no
#echo you select no
goto exit
:exit
#pause
I have improved batch file with yes or no prompt. If user enter any character except y and n , then it will again prompt user for valid input. It Works for me.
#echo off
:ConfirmBox
set /P c= Are you sure want to contine (y/n)?
if /I "%c%" EQU "Y" (
goto :FnYes
) else if /I "%c%" EQU "N" (
goto :FnNo
) else (
goto :InValid
)
:FnYes
echo You have entered Y
goto :END
:FnNo
echo You have entered N
goto :END
:InValid
echo Invalid selection. Enter Y or N
goto :ConfirmBox
:END
pause
exit
/I in if condition will validate both lowercase and uppercase characters.
echo off
setlocal
SET AREYOUSURE = N
:PROMPT
set /P AREYOUSURE=Update Release Files (Y/N)?
if /I %AREYOUSURE% NEQ Y GOTO END
set /P AREYOUSURE=Are You Sure you want to Update Release Files (Y/N)?
if /I %AREYOUSURE% NEQ Y GOTO END
echo Copying New Files
:END
This is code I use regularly. I have noticed in the examples in this blog that quotes are used. If the test line is changed to use quotes the test is invalid.
if /I %AREYOUSURE% NEQ "Y" GOTO END
I have tested on XP, Vista, Win7 and Win8. All fail when quotes are used.
I don't know the platform you're doing this on but I assume Windows due to the .bat extension.
Also I don't have a way to check this but this seems like the batch processor skips the If lines due to some errors and then executes the one with -dev.
You could try this by chaning the two jump targets (:yes and :no) along with the code. If then the line without -dev is executed you know your If lines are erroneous.
If so, please check if == is really the right way to do a comparison in .bat files.
Also, judging from the way bash does this stuff, %foo=="y" might evaluate to true only if %foo includes the quotes. So maybe "%foo"=="y" is the way to go.
Depending on the version of Windows you might find the use of the "Choice" option to be helpful. It is not supported in most if not all x64 versions as far as I can tell.
A handy substitution called Choice.vbs along with examples of use can be found on SourceForge under the name Choice.zip
Add quotation marks (" ") around the %INPUT% so it looks like this:
If "%INPUT%" == "y" goto yes
If "%INPUT%" == "n" goto no
If "%INPUT%" == "Y" goto yes
If "%INPUT%" == "N" goto no
try this for comparision
if "%INPUT%"=="y"...

Identify running instances of a batch file

These are not working for me.
Any help to definitelly corret the four examples below ?
The EXAMPLE01 just echoes "continue", even if I have three CMD.exe opened.
---------- EXAMPLE 01 ------------
#echo off
wmic process where name="cmd.exe" | find "cmd.exe" /c
SET ERRORLEVEL=value if "%value%" GTR 1 (
ECHO This batch is not first
ECHO quitting ...
)
if "%value%" LSS 2 ECHO continue
I am getting the "unexpected i error" message in the EXAMPLE 02!
----------- EXAMPLE 02 -------
#echo off
FOR /F "usebackq tokens=2" %i IN (`tasklist ^| findstr /r /b "cmd.exe"`)
DO taskkill /pid %%i
I am getting the "is first" message in the EXAMPLE 03, even with three CMD.exe opened!
----------- EXAMPLE 03 -------
#echo off
wmic process where name="cmd.exe" | find "cmd.exe" /c
if "%errorlevel%" LEQ 1 echo CMD is first
if "%errorlevel%" GTR 1 echo CMD is already running
It is also possible that I will not have access to the Wmic command at work, so, another possibility is found in the EXAMPLE 04 ... but to no avail.
----------- EXAMPLE 04 -------
#echo off
Tasklist /FI "IMAGENAME eq cmd.exe" 2>NUL | find /I /N "cmd.exe">NUL
if "%ERRORLEVEL%"==0 do (goto Use) else (goto Cont)
:Cont
ECHO Only one instance running
pause
:Use
echo Application running already. Close this window
Kind regards,
Maleck
wmz identified a number of errors in the OP's code, and also has an excellent suggestion to use a lock file for concurrency control.
Below is a robust batch solution that uses a lock file to prevent multiple instances of the batch file from running at the same time. It uses a temporary lock file for the concurrency control. The mere presence of the lock file does NOT stop the script from running. The script will only fail if another process has an exclusive lock on the lock file. This is important in case the script should crash or be killed without deleting the lock file. The next process to run the script will still succeed because the file is no longer locked.
This script assumes the script is installed on a local drive. It allows only one instance for the entire machine. There are lots of variations to control the amount of concurrency allowed. For example, incorporating the %USERNAME% into the lock file name would allow one instance per user in a network environment. Incorporating %COMPUTERNAME% in the name would allow one instance per machine in a network environment.
#echo off
setlocal
:: save parameters to variables here as needed
set "param1=%~1"
:: etc.
:: Redirect an unused file handle for an entire code block to a lock file.
:: The batch file will maintain a lock on the file until the code block
:: ends or until the process is killed. The main code is called from
:: within the block. The first process to run this script will succeed.
:: Subsequent attempts will fail as long as the original is still running.
set "started="
9>"%~f0.lock" (
set "started=1"
call :start
)
if defined started (
del "%~f0.lock" >nul 2>nul
) else (
echo Process aborted: "%~f0" is already running
)
exit /b
:start
:: The main program appears below
:: All this script does is PAUSE so we can see what happens if
:: the script is run multiple times simultaneously.
pause
exit /b
EDIT
The error message "The process cannot access the file because it is being used by another process." can be suppressed by redirecting stderr output to nul in an outer block.
2>nul (
9>"%~f0.lock" (
set "started=1"
call :start
)
)
The problem with the above is that all error messages for the main program will also be suppressed. That can be fixed by 1st saving the current definition of stderr to another unused file handle, and then adding yet another inner block that redirects stderr back to the saved file handle.
8>&2 2>nul (
9>"%~f0.lock" (
2>&8 (
set "started=1"
call :start
)
)
)
You do not set value anywhere. Even if you did it would not work as variables are expanded on parse. You would need to use delayed expansion if you need to both set and test variable against what was set. Quotes on comparison are not required (see 3). help set will show you info delayed expansion.
%i should be %%i. DO taskkill /pid %%i must be on same line as rest of for command. You also use findstr in regex mode, which means it will search for cmd[any character]exe
You use string (lexical) comparison: "1" leq 1 is true (as "4" leq 1 also is...). Use errorlevel 1 which is equivalent to errorlevel equal or greater than 1. help if shows syntax
Same as 3 plus do in do (goto Use) should be removed. %errorlevel%==0 would work, but it's normally advised to use errorlevel number.
How to check if there is only 1 cmd running:
#echo off
for /f %%i in ('Tasklist /FI "IMAGENAME eq cmd.exe" 2^>NUL' ^| find /I /c "cmd.exe"') do (
if %%i leq 2 (echo only one instance) else (echo More than one instance)
)
Note: it's just an example. I do not recommend this as a real method of concurrency control. I would rather use lock file for that.

Something wrong with some batch file stuff, doesn't do what it should do, cant spot syntax errors

So, I am writing a computer speeder in batch, and I am baffled by this problem:
#echo off
cls
color 02
:licence
title Computer Speeder 2012 (Free licence)
cls
echo This is the free version of Computer Speeder 2012.
echo.
echo Do you wish to license this copy? [Y]es or [N]o.
choice /c NY /n
if ERRORLEVEL 2 goto license
if ERRORLEVEL 1 goto startup1
:license
dir | find "validatecode.txt" > if exist "validatecode.txt"
goto bam
) else (
goto else
:bam
cls
echo Type in your lisencing code to validate this
echo copy to upgrade to the full version:
echo.
set /p vd=""
:else
cls
echo You do not have a validation file on your computer,
echo if you purchased the full version you probably
echo deleted the file. Check the recycle bin, check you
echo put it in the correct path or re-purchase the
echo software because it has an individual code.
pause
goto startup1
For some odd reason, it executes all of this code chronologically:
First it asks if you want to license the copy of the software. If you press Y it takes you to license. It then performs the line dir | find "validatecode.txt" >if exist "validatecode.txt"
so on and so forth. However, it goes to bam and THEN goes to else. There's no other flaw in the code, I cant find any syntax error, can you guys explain what I am doing wrong? Im a noob kinda :/
If you want the program to stop after executing :bam, you should put a
goto:EOF after set /p vd=".
:bam
cls
echo Type in your lisencing code to validate this
...
set /p vd=""
goto:EOF
:else
cls
...
Try replacing this:
:lisence
dir | find "validatecode.txt" > if exist "validatecode.txt"
goto bam
) else (
goto else
with this:
:lisence
if exist "validatecode.txt" goto bam
goto else
Explanation:
You would need to test an errorlevel of the Find process for your code to possibly work, and the redirector '>' only creates a file called 'if', containing, oddly, the words exist and vaildatecode.txt.
Your DIR command will only look in the execution directory, but your 'bam' subroutine implies you intend to search the hard drive? True? If so, my 'if exist' command should be changed to:
dir /s/b validatecode.txt && goto bam
You appear to not be using information inside the file, so if you need to search the entire hard drive this command will perform it, and will logically go to the correct subroutine.
The snippet provided tests for a file called validatecode.txt in the local directory, and executes a goto to the label bam if it is found. If the file does not exist, focus drops to the next line, which performs a goto to the label :else.
You will need to finish your script differently as well. As it stands, if the validatecode.txt is found, you will be executing both 'bam' and 'else'. You should place a final label like :exit (:eof is reserved) and make a goto :exit command as the last line of each subroutine.
Finally, your call to the subroutine Startup1 will always fail because you have no label called Startup1.
From a flow standpoint, it looks like you have no way to exit if the user does not want to license the copy they are using.

DOS script to remove identical files by content

I have a directory with several thousand text files that I need to process. Some of these files are identical while others are identical except the timestamp varies by a few seconds / milliseconds. I need some way to automate the deletion of identical files and only keep one copy.
I'm thinking of something like:
while there are files in the directory still
{
get file // e.g., file0001
while (file == file + 1) // e.g., file0001 == file0002 using 'fc' command
{
delete file + 1
}
move file to another directory
}
Is something like this even possible in Microsoft Windows Server 2003's DOS?
Of course it is. Everything is possible in batch. :D
This batch doesn't actually delete files. It just echos the result of the comparison. You can delete either one of the files if you find two that are the same.
Save the code as CleanDuplicates.bat and start the program with CleanDuplicates {Folder}
Provided AS IS, without any guarantees! I don't want you knocking on my door because your entire server is messed up. ;-)
The code actually calls itself recursively. This could maybe be done in a different way but hey, it works. It also starts itself again in a new cmd, because that makes cleaning up easier. I tested the script in Windows Vista Business, but it should work on Server 2003 as well. Hey, it even has a help function. ;-)
It contains two loops that each return every file, so when you implement the actual deleting, it may report that some files don't exist, because they are deleted in an earlier iteration.
#echo off
rem Check input. //, /// and //// are special parameters. No parameter -> help.
if %1check==//check goto innerloop
if %1check==///check goto compare
if %1check==////check goto shell
if %1check==/hcheck goto help
if %1check==check goto help
rem Start ourselves within a new cmd shell. This will automatically return to
rem the original directory, and clear our helper environment vars.
cmd /c %0 //// %1
echo Exiting
goto end
:shell
rem Save the current folder, jump to target folder and set some helper vars
set FCOrgFolder=%CD%
cd %2
set FCStartPath=%0
if not exist %FCStartPath% set FCStartPath=%FCOrgFolder%\%0
rem Outer loop. Get each file and call ourselves with the first special parameter.
for %%a in (*.*) do call %FCStartPath% // "%2" "%%a"
goto end
:innerloop
rem Get each file again and call ourselves again with the second special parameter.
for %%b in (*.*) do call %FCStartPath% /// %2 %3 "%%b"
goto end
:compare
rem Actual compare and some verbose.
if %3==%4 goto end
echo Comparing
echo * %3
echo * %4
fc %3 %4 >nul
rem Get results
if errorlevel 2 goto notexists
if errorlevel 1 goto different
echo Files are identical
goto end
:different
echo Files differ
goto end
:notexists
echo File does not exist
goto end
:help
echo Compares files within a directory.
echo Usage: %0 {directory}
goto end
:end

capturing CMD batch file parameter list; write to file for later processing

I have written a batch file that is launched as a post processing utility by a program. The batch file reads ~24 parameters supplied by the calling program, stores them into variables, and then writes them to various text files.
Since the max input variable in CMD is %9, it's necessary to use the 'shift' command to repeatedly read and store these individually to named variables. Because the program outputs several similar batch files, the result is opening several CMD windows sequentially, assigning variables and writing data files. This ties up the calling program for too long.
It occurs to me that I could free up the calling program much faster if maybe there's a way to write a very simple batch file that can write all the command parameters to a text file, where I can process them later. Basically, just grab the parameter list, write it and done.
Q: Is there some way to treat an entire series of parameter data as one big text string and write it to one big variable... and then echo the whole big thing to one text file? Then later read the string into %n variables when there's no program waiting to resume?
Parameter list is something like 25 - 30 words, less than 200 characters.
Sample parameter list:
"First Name" "Lastname" "123 Steet Name Way" "Cityname" ST 12345 1004968 06/01/2010 "Firstname+Lastname" 101738 "On Account" 20.67 xy-1z 1 8.95 3.00 1.39 0 0 239 8.95
Items in quotes are processed as string variables. List is space delimited.
Any suggestions?
echo %* 1>args.txt
%* references all arguments: %1 %2 %3...
It also works with subroutines.
call :test 1 2 3
goto :eof
:test
echo 1: %1
echo 2: %2
echo 3: %3
echo *: %*
exit /b
output:
1: 1
2: 2
3: 3
*: 1 2 3
See the following website for more information:
http://ss64.com/nt/syntax-args.html
Interesting Post. It sparked my interest.
I too am needing something that could accept parameters and although this probably isn't useful to you now I thought it might be useful at some later date.
My solution is less simple - because there just isn't an elegant way to do it.
Basically, in this example the "-" can be used to identify a parameter, and the next space is assumed to be set to a value.
Legal Stuff:
So this is all my code and I don't really care how or where you choose to use it. No need to cite me it's just an example anyway.
Like this:
Microsoft Batch:Begin Copy below and save as filename.bat
#ECHO OFF
REM USAGE: this-batch-name.bat -BUILD "1.2.3 build 405" -JOB "Running This Job" -run RUN_FUNCTION
SET __CURRENT_WORKING_DIRECTORY__=%~dp1
ECHO.__CURRENT_WORKING_DIRECTORY__=%__CURRENT_WORKING_DIRECTORY__%
REM # Clear Previous Variables
SET PACKAGING_BUILD_NUMBER=
SET PACKAGING_JOB_NAME=
SET GO_DEEPER=
SET RUN_COMMAND=
REM ## In order to read variables set while in a "FOR" loop
REM ## you have to set the 'ENABLEDELAYEDEXPANSION' with 'SETLOCAL'.
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
REM ## Capture Command line parameters here with a %*
FOR %%A IN (%*) DO (
REM ## If we found something with a '-' in previous pass run GO_DEEPER will be defined and thus set to the command line argument.
IF DEFINED GO_DEEPER (
REM ## When ENABLEDELAYEDEXPANSION is Set with setlocal command you have to use exclamation: i.e. '^!'
IF /I "-BUILD"=="!GO_DEEPER!" SET PACKAGING_BUILD_NUMBER=%%A
IF /I "-JOB"=="!GO_DEEPER!" SET PACKAGING_JOB_NAME=%%A
IF /I "-RUN"=="!GO_DEEPER!" SET RUN_COMMAND=%%A
SET SET GO_DEEPER=
)
IF /I "%%A" GEQ "-" (
REM ## Wow we found your command line argument that started with a '-' so set the GO_DEEPER Var
SET GO_DEEPER=%%A
) ELSE (
SET SET GO_DEEPER=
)
)
REM ## Time to grab the variables set while in delayed expansion mode
ENDLOCAL && SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER% && SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME% && SET RUN_COMMAND=%RUN_COMMAND%
REM ## Sucks, but you have to clear the '"' and "'" if it exists.
IF DEFINED RUN_COMMAND (
SET RUN_COMMAND=%RUN_COMMAND:"=%
SET RUN_COMMAND=%RUN_COMMAND:'=%
)
IF DEFINED PACKAGING_JOB_NAME (
SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME:"=%
SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME:'=%
)
IF DEFINED PACKAGING_BUILD_NUMBER (
SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER:"=%
SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER:'=%
)
REM ## Now we can try to run the command function if the -run was used...
IF DEFINED RUN_COMMAND (
CALL:--%RUN_COMMAND% "'%PACKAGING_JOB_NAME%'","'%PACKAGING_BUILD_NUMBER%'"
) ELSE (
ECHO Try running:
ECHO %0 -BUILD "1.2.3 build 405" -JOB "Running This Job" -run RUN_FUNCTION
)
GOTO DONE
:--RUN_FUNCTION
ECHO running... %~0
SET VARPASSED1=%~1
SET VARPASSED2=%~2
IF DEFINED VARPASSED1 ECHO VARPASSED1 was %VARPASSED1%
IF DEFINED VARPASSED2 ECHO VARPASSED2 was %VARPASSED2%
ECHO Add your code to process here...
GOTO:EOF
:DONE
ECHO We got the following results...
IF DEFINED PACKAGING_JOB_NAME ECHO PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME%
IF DEFINED PACKAGING_BUILD_NUMBER ECHO PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER%
IF DEFINED RUN_COMMAND ECHO RUN_COMMAND=%RUN_COMMAND%
</pre> </code>
Microsoft Batch END Copy
RESULTS:
__CURRENT_WORKING_DIRECTORY__=C:\dev\a\win\sysprep\
running... :--RUN_FUNCTION
VARPASSED1 was "'Running...'"
VARPASSED2 was "'This...'"
We got the following results...
PACKAGING_JOB_NAME="Running This Job"
PACKAGING_BUILD_NUMBER="1.2.3 build 405"
RUN_COMMAND=RUN_FUNCTION