How can I tell if my batch file is running?

Solution 1:

I believe a lock file is the simplest reliable solution. The trick is to make sure your running batch process maintains an exclusive write lock on the file until it terminates. The beauty of this system is that Windows will release the lock no matter what reason the batch terminates.

Once you have a lock file, you need a way to detect if the file is currently locked. I describe how to do this at How to check in command-line if a given file or directory is locked (used by any process)?

I've used this primitive, yet effective, technique to accomplish some fairly sophisticated tasks with Windows batch:

  • Parallel execution of a list of tasks, limiting the total number of simultaneous processes
  • How do you have shared log files under Windows?
  • Serialize execution of symstore via Powershell or BATCH

You are not clear where the batch file resides - on the remote machine, or the local machine, or if you might be running the script on multiple machines simultaneously, but only one active process per machine.

If the batch script is on the remote machine, and your process has write access to the script, then you can use the batch file itself as the lock file! You simply need to call a :subroutine while redirecting an unused file handle to the batch script using append mode. The CALL will fail if another process already has a lock. The lock will be released automatically when the script terminates (regardless of how it terminates).

myscript.bat

@echo off
:: Note - this extra call is to avoid a bug with %~f0 when the script
::        is executed with quotes around the script name.
call :getLock
exit /b

:getLock
:: The CALL will fail if another process already has a write lock on the script
call :main 9>>"%~f0"
exit /b

:main
:: Body of your script goes here. Only one process can ever get here
:: at a time. The lock will be released upon return from this routine,
:: or when the script terminates for any reason
exit /b

If your script is on a different machine than the process, but you only have the process running on one machine at a time, then I think the above will still work.

Perhaps a better alternative is to establish a dedicated lock file on each remote machine, separate from the batch script. Then you can run the process on as many remote machines as you want.

Solution 2:

Thanks for the help. I've implemented the following:

Before starting my script (in a wrapper script), check whether the lockfile exists:
  If it does, read the PID out of it.
    If the PID is still a running cmd process, exit my script as another version of it is already running.
    If the PID is not still a running cmd process, delete the lockfile
  Start the script, and create a new lockfile, containing the PID of the started script

When exiting my script, delete the lockfile

That seems to work ok, further testing will tell.

  1. To list running processes I use POWERSHELL Get-Process as TASKLIST uses WMI, which as I stated earlier, is not available to me.
  2. To determine the PID of the started script, I list all the current cmd PIDs, start the script, then list all the cmd PIDs, looking for the one that wasn't there before.