I have a batch script that I use to start and stop a service. The whole script is encapsulated in a subroutine so that i can do output redirect:
call :sub >> C:\Users\Administrator\Desktop\output%LogFilename%.txt
#exit /b 0
:sub
.
start cgsservice.exe -c config\cgs.json
.
The problem that I am having is that after starting the service using the script I CANT run the script again to stop the service. I get the following error:
The process cannot access the file because it is being used by another process.
I know what the issue is but I dont know how to fix it. The issue is the redirect to the log file, since the service is running it wont let me run the script again because it cant access the output%LogFilename%.txt file. Ones I kill the service manually im able to run the script again. Any ideas how to solve this issue with out getting rid of the redirect
You won't be able to "reclaim" the log file while the process is running. Instead you will have to stop the process which has a lock on it. You can automate the stop by adding this line prior to your call to :sub:
REM Stop any running instances of the service.
REM This is needed to release the lock on the log file.
TaskKill /im cgsservice.exe /f
Note this is the same as forcefully terminating the application, so I'm not sure what ramifications it may have. You can try without the /f switch (which will attempt a graceful closing) to see if the process stops this way.
Your other option would be to just have a separate log file for the cgsservice.exe process, but I'm not sure how that would fit into your workflow. To implement this, update the line:
call :sub >> C:\Users\Administrator\Desktop\output%LogFilename%_cgsservice.txt
Note that doing this will cause a lock on the alternate log file until the cgsservice.exe process is terminated. You can use the same TaskKill statement as above to stop it.
If you want to check if cgsservice.exe is not started and then start it, this should do it:
FOR /F "usebackq tokens=* delims=" %%A IN (`TaskList /FI "IMAGENAME eq cgsservice.exe"`) DO (
REM Check against the result of TaskList when no matches are found.
IF "%%A"=="INFO: No tasks are running which match the specified criteria." (
REM Service is not started. Start it now.
START cgsservice.exe -c config\cgs.json
)
)
Related
I have a script that will call other scripts to run then go back to process.
Example:
Call a script to ping and when it completes, go back to script.
My issue is I have a script called Bounce.bat
Here is the code:
#Echo off
set IPADDRESS=127.0.0.1
set INTERVAL=60
:PINGINTERVAL
ping %IPADDRESS% -n 1
timeout %INTERVAL%
GOTO PINGINTERVAL
What I need is the command that will kill the Bounce.bat from inside another batch script.
I was going to try killing CMD but that kills the original script as well.
I Have tried experimenting as well as looking at script examples.
Does anyone know what is needed to kill this bat only?
You can use the Command "taskkill" to stop other processes in Windows.
You can write something like this in your other batch-file:
taskkill /IM MYBATCHFILENAME.bat
You can force to kill the process, if you are using the parameter /f at the end of your command.
I'm trying to start a fixed number of concurrent batch processes that have similar filenames, all in the same directory:
TestMe1.bat
TestMe2.bat
TestMe3.bat
All batches should start at the same time, and all should complete before the batch continues, e.g. a master.bat:
echo Starting batches
(
start "task1" cmd /C "TestMe1.bat"
start "task2" cmd /C "TestMe2.bat"
start "task3" cmd /C "TestMe3.bat"
) | pause
echo All batches have stopped and we can safely continue
I'm trying to find a way to start all batches in the directory that match TestMe*.bat, so that I don't have to craft a new master.bat file each time. Something like this, but, you know, working:
echo Starting batches
(
for /f "delims=" %%x in ('dir /b /a-d TestMe*.bat') do start "task" cmd /C "%%x"
) | pause
echo All batches have stopped and we can safely continue
Thanks to this and this for getting me this far.
Advice and ideas gratefully received!
First you have to understand how this special method of using pipe with the pause command works and why it can be used for waiting for multiple parallel processes.
Taking your first working code sample as the starting point
(
start "task1" cmd /C "TestMe1.bat"
start "task2" cmd /C "TestMe2.bat"
start "task3" cmd /C "TestMe3.bat"
) | pause
It works because each new instance of CMD which is invoked by the start command will be started in a new console with its stdout and stderr redirected to to that new console, so the outputs of the child CMDs will not be redirected to the pipe and so will not be consumed by the pause command which is waiting for input from the pipe.
BUT,
Still each of the processes have inherited the pipe handle, so the handle to the pipe will remain open as long as the child processes are alive.
As a consequence the pause command in the right side of the pipe will remain active, waiting for input from the left side of the pipe until it receives input from the pipe(which will never happen) or all child processes have terminated and closed the handle to the pipe.
So the main CMD instance which is executing your master batch file, is actually waiting for the pause command (right side of the pipe) to terminate which in turn is waiting for child processes (potential pipe writers) to terminate.
Now it becomes clear why your second attempted code involving the FOR loop is not working.
(
for /f "delims=" %%x in ('dir /b /a-d TestMe*.bat') do start "task" cmd /C "%%x"
) | pause
The FOR command inside the pipe is executed by the child CMD in command line mode, In this mode the command echoing in on by default, so every command inside the FOR body will be echoed to standard output(the pipe) before execution, which in turn feeds the pause command and terminate its process before even the first child process is created, Therefor the batch file execution continues without ever waiting for the child processes to finish.
This can be easily resolved by putting the # after do will turn the command echoing off inside the FOR body.
(
for /f "delims=" %%x in ('dir /b /a-d TestMe*.bat') do #start "task" cmd /C "%%x"
) | pause>nul
You may also want to hide the output of pause command by redirecting it to nul
EDIT: It seems I missed the point of this question. Despite that, I think I'll leave this here anyway for other people to find. Feel free to downvote though.
Here are other suggestions on waiting for subordinate processes to complete.
Your main process should periodically check if every subordinate processes is done. There is a multitude of ways of doing that. I'll name a few:
Before that, please insert a delay between checks to not consume all CPU in a tight loop like this:
timeout /t 1 /nobreak
Marker files
Make subordinate processes create or delete a file when they are about to finish
You can create files like this:
echo ANYTHING>FILENAME
Main script should periodically check if those files exist like this:
if exist FILENAME goto IT_EXISTS
When all/none of the files exist your task is complete.
To prevent clutter, create files in a %random% folder inside %temp% directory and pass its name to subordinates via arguments %1, %2 ...
Check process existance by window title
Run tasklist and parse its output to determine if your subordinate programs still run.
Probably the easiest way is to use window name to filter out "your" processes.
Start them like this:
start "WINDOW_TITLE" "BATCH_FILE" ARGUMENTS
then search for them like this:
TASKLIST /fi "Windowtitle eq WINDOW_TITLE" | find ".exe"
if "%errorlevel%" == "0" goto PROCESS_EXISTS
if none are found your task is finished.
Further information can be found at: A, B, C
Check process existance by PID
Instead of window title you can use processes' PID.
To obtain it run your process with WMIC as described here
External programs
You can download or write an external program to facilitate inter-process communication. Examples include:
TCP server and clients with netcat
Use mkfifo from GnuWin32 coreutils to create a named pipe and use it
Windows semaphores and events via custom C/C#/AutoIT program
Utilities such as NirCmd and PsExec may simplify PID checking procedure
....and more
If none of solutions work for you please edit the question to narrow down your query.
Thanks. Thought I'd try writing a batch file to kill another open cmd session that is constantly open churning out lots of scrolling info.
I got a bit carried away and am now outta my league so to speak.
Basically I am trying to search for "£000.00" within a each emerging line of tet that appears in the other running open command window. The running command session is named in the window as C:\windows\system32\cmd.exe but is does have a valid .exe process name in task manager while running and open.
The batch file code below is as far as I've got.
The idea is that when the above string is found in the other process that process/program get closed down them re-launched.
This is as far as I've got.
#echo off
#echo off
title Shut down other process and relaunch
:loop
start /d "C:\Users\Desktop" ActiveDOSprogram.exe
:: #setlocal enabledelayedexpansion
:: #echo off
:: set stringfound=1
find /c "*£000.00*" C:\Windows\system32\cmd.exe && (echo found %date% %time%:~0,-3% >> "%USERPROFILE%\Desktop\Crash_Report.txt"
taskkill /IM ActiveDOSprogram.exe /F
timeout /t 20
start /d "C:\Users\Desktop" ActiveDOSprogram.exe
goto loop
So when I tried this without any variables and in a loop and I think i nearly blew my PC!
Reason I'm stuck is I'm really a novice at this (but trying) and I got as far as I think I need a variable in there somewhere, that only move's to the next line (taskkill+restart) when £000.00 is found in the other process.
Thanks
wingman
I have a series of batch files that execute some action on a windows server, and then call a web url that registers on a database that the actions are completed and when.
I call the web file like this
Start "http://the_url"
It works fine, but i noticed that the browser persist in the background processes, but if I log in with the user there's no opened browser's window.
Ho can I close the browser in the same batch file? There's a better way to call an url?
start the process with WMIC to get its pid:
#echo off
for /f "skip=5 tokens=2 delims==; " %%a in ('wmic process call create "chrome.exe url" ') do (
set "pid=%%a"
goto :break
)
:break
echo %pid%
taskkill /pid %pid% /f
May be better approach is to execute directly a webrequest without a browser.
Check winhttpjs.bat which is capable to execute numerous kind of http requests.
You are going to need the Process ID of the Browser to kill it. This might prove problematic if another browser is already open when the Start command is executed. However it is possible by iterating over the output of tasklist like this answer. Once you have this Process ID(PID) you can call taskkill to end it.
Instead of starting a browser, you might be able to just use a Powershell command such as:
powershell Invoke-WebRequest -Uri "http://the_url/"
If performing a GET on the URL is the only thing required, this should do the trick.
A task called "FireSvc.exe" (McAffee service) keeps interfering with our app upgrades. I put
> taskkill /f /im "FireSvc.exe"
in a batch file. We continuously run this during installs so the software will successfully upgrade. I'm not positive why this service starts back so often. We have to run from the command line, because in the task manager you get "access denied" when trying to kill this task.
My question is, how would you make this run every 20-30 seconds?
We cannot install any type of non-approved software either. So, theres that...
Thanks for any input.
Here's what we use:
:runagain
taskkill /f /im "FireSvc.exe"
timeout /T 5
goto runagain
You can use vbscript's sleep command in a batch file with cscript like so...
First create a vbscript file (.vbs extension) that contains the following code (don't forget to save it with ANSI encoding otherwise it won't work):
Wscript.sleep 2500
Wscript.exit
Create a batch file in the same directory with this code:
#echo off
:Kill
cscript //NoLogo file.vbs
taskkill /f /im "FireSvc.exe"
GoTo Kill
I currently don't have access to a PC to check if it works so let me know what happens. I still think there might be a cleverer alternative... cheers!
Edit: Btw you can also simulate a sleep command with the ping command like so:
ping localhost -n 1 -w 2500 >nul
And if you are using windows vista or above you can use the timeout command.