How can I tell if my batch file is running?

15,657

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:

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.
Share:
15,657

Related videos on Youtube

FrinkTheBrave
Author by

FrinkTheBrave

Updated on September 18, 2022

Comments

  • FrinkTheBrave
    FrinkTheBrave over 1 year

    I have a batch script which is run from an AT command, and may get run more than once. When it starts, I need it to detect if it already running, and if so, exit (the second one) immediately.

    1. It must be robust, and handle if the scripts exits unexpectedly (i.e. so I can't set a flag on entry and clear it on exit)
    2. It must run in a Remote Desktop session
    3. I'm stuck on XP with Powershell v2, but don't mind writing a little exe if I can't do it in batch/powershell or vbs
    4. The script must run minimised, so I start it with Start "NAME" /MIN %COMSPEC% /C "MyScript.bat"
    5. Other cmd windows may be open so I need to check the running script
    6. The batch script runs as SYSTEM user, but I can't use any WMI

    I was using PowerShell Get-Process to look at MainWindowTitle, but this didn't work when remote connecting to the computer as the script may be running, but not displayed in this remote connection instance. I this case, the cmd process is seen by Get-Process, but the MainWindowTitle is blank.

    I've tried Get-Process and looked at the expanded StartInfo.EnvironmentVariables property, but can't see how to create an env var so that it appears in the property.

    I thought about using /WAIT in the start command, then the AT will remain open until it finishes, but the script containing the AT is not minimised

    Any ideas?

    • Admin
      Admin about 8 years
      Use a "lockfile". Create a file when you start the script and delete it when you finish. Before creating it check whether it already exists. If it already exists you are in a second batch file so exit.
    • Admin
      Admin about 8 years
      @DavidPostill I think their first point may be a problem with that, in that if the script fails the lock file won't be removed - Depending on how it fails, I guess.
    • Admin
      Admin about 8 years
      @Jonno That's easily gotten around. if the file already exists and is older than the current time (by some configurable difference) then assume a crash and delete the lock file. Then create a new one.
    • Admin
      Admin about 8 years
      @DavidPostill Could be a solution - OP, do you have any more information on what your scripts are actually doing? Should it be complete within seconds, minutes, hours? Is it invariable enough that a time scope could be used? How can it fail - would it likely be gracefully enough that a delete on the lock could be handled after a failure regardless, EG, if it fails does it at least close itself, so you could call a batch to open a lock, run your script, then delete your lock, regardless of the script result?
    • Admin
      Admin about 8 years
      Thanks for your input. The script could take hours, I'm afraid, and batch errors aren't really trappable
    • Admin
      Admin about 8 years
      One other thought - you specify the title with the "NAME" command in start but it doesn't work in some situations. What if you use the command title name in your batch file, does it carry across then? EG, add it as the first line of your batch and then don't specify it from your start command.
    • Admin
      Admin about 8 years
      You could also use the PID as the lockfile filename. If the PID doesn't match exit the second batch file. See how to get own process pid from the command prompt in windows for code to get the PID. That script also uses a lock "The script establishes an exclusive lock on a temporary file that incorporates the current time into the name. There can only be a collision if two like named batch processes attempt to get the PID within the same 0.01 second time interval, in which case only one will succeed."
    • Admin
      Admin about 8 years
      @Jonno no, it doesn't matter how the title is set, the Get-Process information doesn't include it when it's running in another session
    • Admin
      Admin about 8 years
      @FrinkTheBrave What's wrong with lockfile approach? Note my second comment about handling a crash, and my third comment about using the PID.
    • Admin
      Admin about 8 years
      @DavidPostill I'll look into a lockfile. If I store the PID in it, I can check for that process still running. If it's not, then it must have exited unexpectedly. I can't use your time idea as it could be hours, and I can't use that PID link as it uses WMI. I'll think of a way to get the PID. Maybe list all the CMD PIDs, start my cms and see what PID has been added. Or write a little vbs launcher script...
  • FrinkTheBrave
    FrinkTheBrave about 8 years
    Thanks for the thorough answer! but I used an alternative as I didn't fancy the idea of a write locked file
  • Mark Deven
    Mark Deven over 5 years
    lol why did I get down votes on this