8

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?

  • 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. – DavidPostill Feb 3 '16 at 10:43
  • @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. – Jonno Feb 3 '16 at 10:43
  • @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. – DavidPostill Feb 3 '16 at 10:45
  • @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? – Jonno Feb 3 '16 at 10:48
  • 1
    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." – DavidPostill Feb 3 '16 at 10:52
7

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.

  • Thanks for the thorough answer! but I used an alternative as I didn't fancy the idea of a write locked file – FrinkTheBrave Feb 4 '16 at 13:34
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.
-2

I've created an automated plugin called CMDS you can use here: How would I see if a certain Batch File is running and get the PID? However Im not sure if this will work with XP.

Example Command: CMDS /TS "Your Batch File Title"

It also has a gui mode for being used by itself.

Here is the help file:

Help msg

  • lol why did I get down votes on this – Mark Deven Jan 19 at 14:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.