I am making a web controller for a python service, so that a user can start and stop the service. I need to be able to know whether the service is running.
I want to check whether python is running my script from cmd. echo 1 if it is running my script otherwise echo 0
such as:
if (python is running bot.py) (
echo 1
)
It is a bit tricky in plain cmd, but this seems to work:
#echo off
tasklist.exe /V /FI "IMAGENAME eq cmd.exe" /FO LIST | find "bot.py" >nul
if ERRORLEVEL 1 (
echo 0
) else (
echo 1
)
You would probably be best off having a file that is created by the running bots, which contains their PID, and then checking for the presence of this file. It would allow you to get the PID, but it does rely on the processes being terminated cleanly.
This works similarly to lock files, which can be used to stop 2 instances of a program running on the same data at the same time.
Related
I have a .bat script that attempts to start a Windows service at the end.
:start_wildfly
echo.
set /p wildfly_service_name="Enter Wildfly service name: "
echo INFO: Starting %wildfly_service_name%...
echo.
call net start "%wildfly_service_name%"
I want to be able to interpret the result of the net start attempt so that I can have my script take the appropriate action if it fails (e.g. if the service is already running, restart it. If the service name is invalid, re-prompt for the name again, if the user doesn't have sufficient privileges, exit).
The problem is that the NET command does not return the documented Win32_Service class codes.
It does echo errors on the console, however:
The requested service has already been started.
More help is available by typing NET HELPMSG 2182.
See http://ss64.com/nt/net_service.html for a list of the errors.
Unforunately, the errorlevel variable is always 2 in these error cases, so I can't rely on that.
What I'm now trying to do is run a FIND on the output of the NET command, searching for specific error codes and act upon them.
net start Wildfly 2>&1 | FIND "2182"
if %errorlevel% equ 0 goto service_already_running
So, the result of the FIND is stored in errorlevel and I can check to see if the FIND succeeded by checking if errorlevel is 0. This works.
Now, the problem comes when I want to check for more than one error code. I don't know how to expand the code above to check for "2185" as well, for example, and goto a different label in that case.
I'm now attempting to store the entire result of the NET command into a variable, and then run a FINDSTR on that variable.
setlocal EnableDelayedExpansion
set "output_cnt=0"
for /F "delims=" %%f in ('dir /b') do (
set /a output_cnt+=1
set "output[!output_cnt!]=%%f"
)
for /L %%n in (1 1 !output_cnt!) DO echo !output[%%n]!
This should store and echo each line of the output, however the last line doesn't seem to do anything.
And then I've also found some code that should search within a variable and return whether or not that string was found:
echo.%output%|findstr /C:"2182" >nul 2>&1 && echo Found || echo Not found.
I've had no luck putting it all together though. I just want to be able to interpret the result of the NET START <SERVICE> and jump to certain labels based on the result.
I want to be able to interpret the result of the net start attempt
so that I can have my script take the appropriate action if it fails (e.g. if the service is already running, restart it. If the service name is invalid, re-prompt for the name again, if the user doesn't have sufficient privileges, exit).
Start the service as you are already doing:
net start "%wildfly_service_name%"
Now check the status of the service.
There are two ways to do this.
Use net start again to see if the service is running:
net start | find "%wildfly_service_name%" > nul
if errorlevel 1 echo The service is not running
Use sc (Service Control) to check the service status:
SC query %wildfly_service_name% | find "STATE" | find "STOPPED"
Or
sc query %wildfly_service_name% | find "STATE" | find "RUNNING"
The two statements above will return %errorlevel% = 1 if the text is not found.
Further Reading
An A-Z Index of the Windows CMD command line - An excellent reference for all things Windows cmd line related.
net - The NET Command is used to manage network resources.
sc - Service Control - Create, Start, Stop, Query or Delete any Windows SERVICE.
Taking DavidPostill's answer of using net start to check the status of the service, here is my new solution:
echo.
echo INFO: Starting %wildfly_service_name%...
echo.
:verify_not_running
net start | find "%wildfly_service_name%" > nul
if %errorlevel% equ 0 goto restart_wildfly
:start_wildfly
net start "%wildfly_service_name%"
goto verify_running
:restart_wildfly
echo The %wildfly_service_name% service is already running. Will now restart...
net stop "%wildfly_service_name%"
net start "%wildfly_service_name%"
:verify_running
net start | find "%wildfly_service_name%" > nul
if errorlevel 1 goto start_wildfly
This script will first verify the service is not running.
If the service is already running, it will restart the service.
In either case, I check at the end to make sure the service is now started. If not, repeat the process over again.
Note that I no longer have a requirement to check that the service name was valid. The service name is now hardcoded earlier in the script so it is assumed to be correct.
And to handle the case of insufficient privileges, I added this snippet at the beginning of the script:
:check_permissions
net session >nul 2>&1
if errorlevel 1 (
echo.
echo ERROR: This script must be run as an Administrator. Please re-run the script from an elevated command prompt.
echo.
echo Right-click "cmd.exe" from the Start menu and select "Run As Administrator".
exit /b %error_level%
)
You're right; the net command apparently always returns 2 for any kind of error. However, you can use the sc start command as a drop-in replacement for net start, and that one does indicate different errors through distinct exit statuses, in particular 1056 for An instance of the service is already running. So, you can use use
sc start "%wildfly_service_name%"
And then check %errorlevel% afterwards.
can someone think of a solution for something like this? :
Program/script logic: It would constantly monitor the windows OS for a process starting within it (***1.exe) (I guess it could constantly run via task scheduler to do the constant monitoring?) , while it sees that ***1.exe is running, it would kill/end another process ***2.exe, and once ***1.exe would go away, it would no longer be stopping the ***2.exe process.
I think it could be either a bash script, powershell script, or a windows service?
Thanks!!!
You can use the Register-CimIndicationEvent cmdlet to register for events raised by Win32_ProcessStartTrace WMI class:
# Define which events to listen for
$NewProcessQuery = "SELECT ProcessId,ProcessName FROM Win32_ProcessStartTrace WHERE ProcessName LIKE '%1.exe'"
# Define the code to run every time a new process is created
$ProcessAction = {
# See if any instances of *2.exe processes are running
if(($TargetProcess = Get-CimInstance -ClassName Win32_Process -Filter "Name LIKE '%2.exe'"))
{
# Terminate them
$TargetProcess |Invoke-CimMethod -MethodName Terminate
}
}
# Register for the event
Register-CimIndicationEvent -Query $NewProcessQuery -SourceIdentifier ProcessCreated
So since the solution above was for only windows 2012 and up, I decided to try another solution. This should work for regular processes, but I'll have to try something else rather than %ERRORLEVEL% because the process I'm monitoring is originally an msi installer and seems like it returns and errorlevel of 1 all the time (running or not) while regular processes return 0 or 1 depending on the status. The process I'm ending starts back up automatically, that's the reason there's no start service command included in here, timeout was set to 62 seconds because the service starts back up automatically every 60 seconds, a /NOBREAK can be added if wanted to eliminate the possibility of user input starting it (if this would be ran without a task scheduler,etc.)
:loop_check
TIMEOUT /T 62
TASKLIST /FI "IMAGENAME eq process.exe" 2>NUL | find /I /N "process.exe">NUL
IF "%ERRORLEVEL%"=="0" (
GOTO stop_process2
) ELSE (
GOTO loop_check
)
:stop_process2
ECHO killing task
TASKKILL /F /IM process2.exe
GOTO loop_check
Read my previous reply/comment before this one for more clarity. This is the final solution that worked for me. A star(*) is included at the end of the 'BeginningOfApplicationName' because the installer/msi I'm detecting has sometimes different names based on it's version, so it finds/finishes the ending (wildcard). Since the name of the process I'm monitoring can have different names, I couldn't compare it to a static string, so I'm comparing it to INFO: , seems thats what windows (2008 and 2012!) both print out when a process is not found.
#ECHO OFF
SETLOCAL EnableExtensions
:loop_check
TIMEOUT /T 62
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq BeginningOfApplicationName*"') DO IF %%x == INFO: (
GOTO loop_check
) ELSE (
GOTO stop_process
)
:stop_process
TASKKILL /F /IM process.exe
GOTO loop_check
I'm trying to uninstall a program EXE via batch file and am not having any success.
The uninstall string found in the registry is as follows:
C:\PROGRA~1\Kofax\Capture\ACUnInst.exe /Workstation
C:\PROGRA~1\Kofax\Capture\UNWISE.EXE /U
C:\PROGRA~1\Kofax\Capture\INSTALL.LOG
If I run that from CMD or batch it does nothing.
If I run C:\PROGRA~1\Kofax\Capture\UNWISE.EXE /U from CMD it will open up a dialog box to point to the INSTALL.LOG file and then proceed to uninstall.
At the end, it will ask me to click finish.
I need this to be silent, can you point me in the right direction? This is on XP and 7.
Every program that properly installs itself according to Microsoft's guidelines makes a registry entry in either HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall (for machine installs) or HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall (for user profile installs). Usually, the key for the program will be its GUID, or else the name of the program. Within that key will be an entry called UninstallString. This contains the command to execute to uninstall the program.
If you already know ahead of time what you will be uninstalling, it should be easy enough to just put that in your batch file. It gets tricky when you try to automate that process though. You can use the reg command to get data from the registry, but it returns a lot of text around the actual value of a given key, making it hard to use. You may want to experiment with using VBscript or PowerShell, as they have better options for getting data from the registry into a variable.
This might help you further.....
How to Create a script via batch file that will uninstall a program if it was installed on windows 7 64-bit or 32-bit
I've had the same problem and this is what I came up with.
Before you start using this method though, you might wanna look up the name of the application on WMIC using CMD so..
First you wanna do: WMIC product > C:\Users\"currentuser"\Desktop\allapps.txt
I'd recommend to output the command to an TXT file because it's really confusing to read it in the Cmd prompt, plus is easier to find the data you are looking for.
Now what you wanna do is find the actual name of the app... If you look at the code I put in, the app name says SkypeT because skype has "™" in the end of it and the command prompt can't interpretate that as it is.
After you got the app name, just put in the find in the 4th line and substitute, a few lines which contain my examples with skype...
Also you can probably creat a variable called %APP% and not worry as much, but at it's current it works just fine...
One thing to note! with me the msi /quiet command did not work, the program would not install or uninstall so I used /passive, which lets the users see what's going on.
#Echo off
CD %cd%
:VerInstall
for /f "tokens=12,*" %%a in ('wmic product list system ^| Find /I "SkypeT"') do (
if Errorlevel = 0 (
Echo Skype is installed! )
if Errorlevel = 1 ( Echo Skype is not installed, proceding to the installation!
Ping localhost -n 7 >nul
goto :Reinstall )
)
:Status
tasklist /nh /fi "IMAGENAME eq "APP.exe" | find ":"> nul
if errorlevel = 1 goto :force
goto :Uninstall
:Force
echo We are killing the proccess... Please do not use the application during this process!
Ping localhost -n 7 > nul
taskkill /F /FI "STATUS eq RUNNING" /IM APP* /T
echo The task was killed with success! Uninstalling...
Ping localhost -n 7 > nul
:Uninstall
cls
for /f "tokens=12,*" %%a in ('wmic product list system ^| Find /I "SkypeT"') do (
set %%a=%%a: =%
msiexec.exe /x %%a /passive /norestart
)
:DoWhile
cls
Tasklist /fi "IMAGENAME eq msi*" /fi "STATUS eq RUNNING" | Find ":" >nul
if errorlevel = 1 (
echo Installation in progress
Goto :DoWhile
)
echo Skype is Uninstalled
:Reinstall
msiexec.exe /i SkypeSetup.msi /passive /norestart
:reinstallLoop
Tasklist /fi "IMAGENAME eq msi*" /fi "STATUS eq RUNNING" | Find ":" >nul
if errorlevel = 1 (
echo Installation in progress
goto :reinstallLoop
)
echo Skype is installed
:end
cls
color 0A
Echo Done!
exit
One last thing. I used this as an Invisible EXE task, so the user couldn't interact with the command prompt and eventually close the window (I know, I know, it makes the whole echoes stupid, but it was for testing purposes).for that I used BAT to EXE converter 2.3.1, you can put everything to work on the background and it will work very nicelly. if you want to show progress to users just write START Echo "info" and replace the info with whatever you want, it will open another prompt and show the info you need.
Remember, Wmic commands sometimes take up to 20 seconds to execute since it's querying the conputer's system, so it might look like it's doing nothing at first but it will run! ;)
Good luck :)
We needed a batch file to remove a program and we couldn't use programmatic access to the registry.
For us, we needed to remove a custom MSI with a unique name. This only works for installers that use msi or integrate such that their cached installer is placed in the Package_Cache folder. It also requires a unique, known name for the msi or exe. That said, it is useful for those cases.
dir/s/b/x "c:\programdata\packag~1\your-installer.msi" > removeIt.bat
set /p RemoveIt=< removeIt.bat
echo ^"%RemoveIt%^" /quiet /uninstall > removeIt.bat
removeIt.bat
This works by writing all paths for 'your-installer.msi' to the new file 'removeIt.bat'
It then assigns the first line of that bat file to the variable 'RemoveIt'
Next, it creates a new 'removeIt.bat' that contains the path/name of the .msi to remove along with the needed switches to do so.
Finally, it runs the batch file which executes the command to uninstall the msi. This could be done with an .exe as well.
You will probably want to place the 'removeIt.bat' file into a known writable location, for us that was the temp folder.
I am trying to write a windows batch script which detects if it runs under system user to launch an other application. The application needs to run as system. But the batch file launching it will be called by an Admin user.
Here is what I have now:
IF "%USERPROFILE:~-13%" == "systemprofile" (
PUSHD "%~dp0\.."
CALL "init some variables"
CALL "my command" %*
POPD
) ELSE (
FOR %%X IN (psexec.exe) DO (SET FOUND=%%~$PATH:X)
IF DEFINED FOUND (
CALL psexec -s %0 %*
) ELSE (
ECHO Must be run as user SYSTEM
ECHO If psexec is in PATH, it will be automatically used.
)
)
For now I check %USERPROFILE% but I guess it is a bad way to do that.
When launching my script with psexec -s, echo %USERNAME% gives the name of the server with a $ at the end.
What is the best way to know if the batch script is running on system user?
Maybe there is a better alternative to achieve that?
Thank you
If you mean running with administrator privileges you can check with something like this:
OPENFILES >nul 2>nul
IF ERRORLEVEL 1 (
COLOR CF
ECHO.You must 'Run as administrator'
PAUSE
GOTO :eof
)
Note that the
2>nul is needed because 2012 Server and Windows 8 return ERRORLEVEL 0 but output "ERROR: Unable to retrieve data."
I don't think it is right that a command returns an error code of 0 but still outputs to STDERR, but...
I would like to be able to query whether or not a service is running from a windows batch file. I know I can use:
sc query "ServiceName"
but, this dumps out some text. What I really want is for it to set the errorlevel environment variable so that I can take action on that.
Do you know a simple way I can do this?
UPDATE
Thanks for the answers so far. I'm worried the solutions that parse the text may not work on non English operating systems. Does anybody know a way around this, or am I going to have to bite the bullet and write a console program to get this right.
sc query "ServiceName" | find "RUNNING"
Let's go back to the old school of batch programing on windows
net start | find "Service Name"
This will work everywhere...
if you don't mind to combine the net command with grep you can use the following script.
#echo off
net start | grep -x "Service"
if %ERRORLEVEL% == 2 goto trouble
if %ERRORLEVEL% == 1 goto stopped
if %ERRORLEVEL% == 0 goto started
echo unknown status
goto end
:trouble
echo trouble
goto end
:started
echo started
goto end
:stopped
echo stopped
goto end
:end
You could use wmic with the /locale option
call wmic /locale:ms_409 service where (name="wsearch") get state /value | findstr State=Running
if %ErrorLevel% EQU 0 (
echo Running
) else (
echo Not running
)
Thinking a little bit outside the box here I'm going to propose that powershell may be an answer on up-to-date XP/2003 machines and certainly on Vista/2008 and newer (instead of .bat/.cmd). Anyone who has some Perl in their background should feel at-home pretty quickly.
$serviceName = "ServiceName";
$serviceStatus = (get-service "$serviceName").Status;
if ($serviceStatus -eq "Running") {
echo "Service is Running";
}
else {
#Could be Stopped, Stopping, Paused, or even Starting...
echo "Service is $serviceStatus";
}
Another way, if you have significant investment in batch is to run the PS script as a one-liner, returning an exit code.
#ECHO off
SET PS=powershell -nologo -command
%PS% "& {if((get-service SvcName).Status -eq 'Running'){exit 1}}"
ECHO.%ERRORLEVEL%
Running as a one-liner also gets around the default PS code signing policy at the expense of messiness. To put the PS commands in a .ps1 file and run like powershell myCode.ps1 you may find signing your powershell scripts is neccessary to run them in an automated way (depends on your environment). See http://www.hanselman.com/blog/SigningPowerShellScripts.aspx for details
#ECHO OFF
REM testing at cmd : sc query "MSSQLSERVER" | findstr RUNNING
REM "MSSQLSERVER" is the name of Service for sample
sc query "MSSQLSERVER" %1 | findstr RUNNING
if %ERRORLEVEL% == 2 goto trouble
if %ERRORLEVEL% == 1 goto stopped
if %ERRORLEVEL% == 0 goto started
echo unknown status
goto end
:trouble
echo Oh noooo.. trouble mas bro
goto end
:started
echo "SQL Server (MSSQLSERVER)" is started
goto end
:stopped
echo "SQL Server (MSSQLSERVER)" is stopped
echo Starting service
net start "MSSQLSERVER"
goto end
:erro
echo Error please check your command.. mas bro
goto end
:end
I would suggest
WMIC Service WHERE "Name = 'SericeName'" GET Started
or WMIC Service WHERE "Name = 'ServiceName'" GET ProcessId (ProcessId will be zero if service isn't started)
You can set the error level based on whether the former returns "TRUE" or the latter returns nonzero
sc query "servicename" | findstr STATE
for example:
sc query "wuauserv" | findstr STATE
To report what the Windows update service is doing, running/paused etc.This is also for Windows 10. Thank me later.
Try
sc query state= all
for a list of services and whether they are running or not.
I've found this:
sc query "ServiceName" | findstr RUNNING
seems to do roughly the right thing. But, I'm worried that's not generalized enough to work on non-english operating systems.
Just to add on to the list if you are using Powershell.
sc.exe query "ServiceName" | findstr RUNNING
The command below does not work because sc is an alias to Set-Content within Powershell.
sc query "ServiceName" | findstr RUNNING
find also does not work on Powershell for some reason unknown to me.
sc.exe query "ServiceName" | find RUNNING
SERVICO.BAT
#echo off
echo Servico: %1
if "%1"=="" goto erro
sc query %1 | findstr RUNNING
if %ERRORLEVEL% == 2 goto trouble
if %ERRORLEVEL% == 1 goto stopped
if %ERRORLEVEL% == 0 goto started
echo unknown status
goto end
:trouble
echo trouble
goto end
:started
echo started
goto end
:stopped
echo stopped
goto end
:erro
echo sintaxe: servico NOMESERVICO
goto end
:end
I noticed no one mentioned the use of regular expressions when using find/findstr-based Answers. That can be problematic for similarly named services.
Lets say you have two services, CDPUserSvc and CDPUserSvc_54530
If you use most of the find/findstr-based Answers here so far, you'll get false-positives for CDPUserSvc queries when only CDPUserSvc_54530 is running.
The /r and /c switches for findstr can help us handle that use-case, as well as the special character that indicates the end of the line, $
This query will only verify the running of the CDPUserSvc service and ignore CDPUserSvc_54530
sc query|findstr /r /c:"CDPUserSvc$"
Use Cygwin Bash with:
sc query "SomeService" |grep -qo RUNNING && echo "SomeService is running." || echo "SomeService is not running!"
(Make sure you have sc.exe in your PATH.)
I have created one based from above but will show if the service is installed first then get whether it is running or not.
sc query "YourService" | find /i "failed" 2>&1>nul && echo.'YourService Not Installed' || (sc query "YourService"| find /i "running" 2>&1>nul && echo.Yes || echo.No)