Batch files are a convenient way to automate repetitive tasks on Windows systems. However, in certain scenarios, it is crucial to ensure that a batch file can only be executed one at a time to avoid conflicts or data corruption. In this blog post, we will explore a technique to enforce single execution of a batch file using a locking mechanism.
To begin, open a text editor and create a new file. Save it with a “.bat” extension, giving it a descriptive name that reflects its purpose. For this example, let’s use “singleuser.bat”. I have also posted a batch file on GitHub Gists to download, if desired.
To enforce single execution, we need to implement a locking mechanism within the batch file. We can achieve this by obtaining a lock to the batch file itself. Here’s an example implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @echo off :: Workaround for expansion issue call :getLock %1 %2 %3 exit /b :getLock :: Open this batch file in append mode, preventing others from doing so call :singleuser-main %1 %2 %3 9>>"%~f0" exit /b :singleuser-main :: Body of your script goes here. Only one process at a time can execute. :: The lock will be released upon return from this routine, or when the batch file terminates for any reason echo %* pause
The first call statement is there to avoid a bug with %~f0 when the script is executed with quotes around the script name. (For more info and some interesting comments, see this GitHub Issue) This work-around simply ensures that the
"%~f0" syntax correctly expands to the double-quoted, fully qualified path of the batch file.
9>>is the magic that starts a new instance at the
singleuser-mainlabel while redirecting output from the special file handle #9 to the batch file itself, opening the batch file in Append Mode. This will fail if the file is already open. This actually does not redirect anything to the file as there is no output coming from #9 (this is just a trick to establish a write lock on the batch file.)
%~f0expands to the fully qualified path of the current batch file
%1 %2 %3are example command-line paramters. You can forward zero, one or more parameters to these subroutines as required.
- The Windows command processor interprets a single digit followed by a redirection symbol as a request to redirect to that specific file handle, with 0, 1, and 2 being used as Standard Input, Standard Output, and Standard Error. In PowerShell, this concept was extended to use 3 to 6 for Warning, Verbose, Debug, and Information output types. (Since 7 through 9 are currently undefined and unused, we specify 9 here.)
- Dating back many years, you could always echo text to files like this:
echo TestMe>myfile.txtwhich generates a file called
myfile.txtthat has one line of text in it with the content of
- With this new redirection trick being introduced with Windows NT, they had to come up with a way to tell the difference between a single digit representing a file handle redirection or simple textual output, so they introduced a caret prefix. For those few unlucky people that wanted to continue to echo a single digit to a text file they now had to prefix their single digit with a caret, such as
echo ^1>myfile.txtwhich writes the single digit
myfile.txt. If you do not include a caret, then the single digit is assumed to be a file handle. I wonder how many people since then have successfully executed the command
echo something>somefile.txtand are unfortunate enough to stumble upon this quirk when “something” just happens to be a single numeric digit. They could be writing numeric results with
echo 10>somefile.txtworking as expected and then notice that
- Here is a test: what does the
somefile.txtfile contain after you run the following command?
- Redirection example, this command
DIR myfilenametofind.txt 1>filelist.txt 2>error.txtcreates a file called
filelist.txtfor the output from the DIR command and another file called
error.txtfor any error messages (such as File Not Found.)
This is where you can place the body of your script and only one instance will be allowed at a time. In this example, we execute a simple
pause command to help demonstrate the effect of single execution. Replace this with your own script logic. We also use
echo %* which is a short-cut to display the values of all command line parameters.
To run the batch file, simply double-click on it in Explorer, and it will execute within a console window. If you attempt to run a second instance, you will receive the error
Another program is currently using this file.
Try editing the file in Notepad and saving changes while the batch file is running. Notepad will also give you an error:
The process cannot access the file because it is being used by another process.
Using the tips outlined in this blog post, you can create batch files that enforce single execution to help automate tasks reliably on Microsoft Windows. One potential use is to enforce single-user execution with background services or scheduled tasks which execute a specific batch file that could also be executed manually by interactive users.
AI image generation is becoming quite popular and I used Bing (powered by DALL-E) to create the image for this post.