I'm working on an unattended automated install of SQL 2008, 2012 and 2014 which so far is working with my batch command. The one issue I'm encountering is that in order to use a core function found in each program (management studio) properly on Windows 10, it needs to run as an admin.
In Windows 10 this can be done manually by opening the file location of the program shortcut - right click - properties - "compatibility" tab - "change settings for all users" - check the box "run this program as an administrator". Is there a way to have a batch command check that box? This way staff won't need to manually run it as an admin each time, it'll just open Management Studio automatically as an admin.
My batch command can be found below to automate the install. The beginning finds the installation files, sets the sa password to whatever I want and pulls the custom settings for the SQL installer from the configurationfile.ini file. I need the "run as an admin" for all users to run after all that.
start "" "%~dp0SQL2008\setup.exe" /SAPWD="XXXXXXXX" /ConfigurationFile="ConfigurationFile.ini" /quiet
#echo off
setlocal
call :reg_compat "C:\User\Test\test.exe" "WINXPSP3 RUNASADMIN"
if errorlevel 1 echo Registry write failed.
exit /b
:reg_compat fullpath, reg_data
setlocal
set "reg_key=HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
set "reg_data="
:: Get current registry data.
for /f "tokens=1,2*" %%A in ('2^>nul reg query "%reg_key%" /v "%~1"') do (
if /i "%%~A" == "%~1" set "reg_data=%%~C"
)
:: Write new entry and exit.
if not defined reg_data (
>nul reg add "%reg_key%" /v "%~1" /d "%~2" /f
if errorlevel 1 exit /b 1
exit /b 0
)
:: Copy original registry data.
set "reg_data_original=%reg_data%"
:: Append new data if not in original registry data.
for %%A in (%~2) do (
set "value_exist="
for %%B in (%reg_data_original%) do (
if "%%~A" == "%%~B" set "value_exist=1"
)
if not defined value_exist (
call set "reg_data=%%reg_data%% %%~A"
)
)
:: Continue only if registry data is modified.
if "%reg_data_original%" == "%reg_data%" exit /b 0
:: Write modified entry and exit.
>nul reg add "%reg_key%" /v "%~1" /d "%reg_data%" /f
if errorlevel 1 exit /b 2
exit /b 0
The code is set to write a test entry. The test entry will add
Windows XP SP3 and Run As Admin compatibility for that filepath.
For actual use change the arguments to call the label :reg_compat
with the fullpath to the file as the 1st argument and the
reg_data compatibility arguments as the 2nd argument. The
compatibility arguments are uppercase and separated with a space.
The label :reg_compat will write new entries or update entries with
adding new compatibility arguments. The errorlevel from the called
label is set to not 0 if reg add fails.
Minimal code instead if you need just that:
#echo off
setlocal
set "reg_key=HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
reg add "%reg_key%" /v "C:\User\Test\test.exe" /d "WINXPSP3 RUNASADMIN" /f
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"
)
I'm running DOS 6.0.6002 on a windows server enterprise system, SP2.
SQL Server 2008 R2 (10.50.4000)
I have a main control program in DOS.
I'm invoking an sql program through sqlcmd.
A simplified version looks like this:
set sqlsvr=myServer
set logfile=logfile.txt
sqlcmd -S %sqlsvr% -d myDB -i import_some_stuff.sql > "%logfile%" 2>&1
echo error level = %ERRORLEVEL%
I need this program to be pretty robust. It has to run every day against a lot of files and tables. If it fails, I need to catch it and notify sysadmin. For now, just catch it.
So to test this, I've tried the following tests:
1) Renaming the file to one that does not exist.
Result: it returns and errorlevel of 1 (that is it caught the error!) bravo!
2) typing in some syntactical rubbish at the front of the sql program.
Result: it prints the error message in the log file, BUT it DOES NOT return an error (so the return value in %ERRORLEVEL% is zero. This seems incredible to me. What am I missing?
Try the -b option to sqlcmd:
-b
Specifies that sqlcmd exits and returns a DOS ERRORLEVEL value when an
error occurs.
The value that is returned to the DOS ERRORLEVEL
variable is 1 when the SQL Server error message has a severity level
greater than 10; otherwise, the value returned is 0. If the -V option
has been set in addition to -b, sqlcmd will not report an error if the
severity level is lower than the values set using -V. Command prompt
batch files can test the value of ERRORLEVEL and handle the error
appropriately. sqlcmd does not report errors for severity level 10
(informational messages).
If the sqlcmd script contains an incorrect comment, syntax error,
or is missing a scripting variable, ERRORLEVEL returned is 1.
Here is the documentation
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"...
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.