How to get an exclusive lock on a file using batch file? - windows

I have a project I need to monitor a batch file which constantly runs to see if its still working. I have a remote machine which needs to monitor this batch file running on another server.
What i need to do is have the batch file create and exclusively lock a text file (can be empty, can be full it does not matter). This is so I can poll it from my remote machine (using an exe created by c#) to see if there is exclusive lock on the file - if so, then do nothing. If can get a lock, then raise alarm (as the batch has failed).
Understand this is probably not the best approach, but unfortunately its what I have to go with. So, is there a way to exclusively lock a file (automatically) using a batch file?

I was skeptical about this initially, but it turns out it can be done by using file redirection. Consider this example:
#echo off
if '%1' == '-lock' (
shift
goto :main
)
call %0 -lock > lockfile.txt
goto :eof
:main
echo %DATE% %TIME% - start
TREE C:\
echo %DATE% %TIME% - finish
goto :eof
Whilst the above batch is running, it is not possible to delete lockfile.txt.
Essentially, the batch checks for a '-lock' parameter. If it's not present, it re-executes itself with the -lock parameter and re-directs it's own output to lockfile.txt
It's also possible to create locks for 'critical' sections within a batch e.g.
#echo off
echo %DATE% %TIME% - started
(
echo Starting TREE
tree c:\
echo TREE finished
) > lock2.lock
echo %DATE% %TIME% - finished
Sources:
How do you have shared log files under Windows?
http://www.dostips.com/forum/viewtopic.php?p=12454

Here is a plain vanilla batch file to lock a particular file temporarily. Edit the txt path accordingly.
#ECHO OFF
powershell.exe -command "$lock=[System.IO.File]::Open('C:\test.txt','Open','ReadWrite','None');Write-Host -NoNewLine 'Press any key to release the file...';$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')"
It unlocks when you press the any key.

Related

Schedule batch file not renaming file to name specified using %date% and %time% variables

As part of a regular file upload process we run a .bat file via Windows Task Scheduler. It opens WinSCPand runs it using a config file.
Then it cds to the file where the upload is stored, renames it, then moves it to the archive file.
If I run the program manually with a pause before the exit, it works fine. Currently is just dumping the file from upload to the archive without renaming it with time and date appended.
#echo off
"C:\Program Files (x86)\WinSCP\winscp.com" /script=CONFIG.txt
cd C:\SCHEDULEDQUERIES\PressGaney\Upload
ren *.csv CL6019_%time:~0,2%%time:~3,2%%date:~-10,2%%date:~3,2%%date:~-4,4%.csv
move *.csv C:\SCHEDULEDQUERIES\PressGaney\archive
exit
Thanks. Happy to give any further details that may be needed.
For lack of further information, I'd suggest
ren *.csv "CL6019_%time:~0,2%%time:~3,2%%date:~-10,2%%date:~3,2%%date:~-4,4%.csv"
should cure the problem. If not, echo this line and then pause the script.
Perhaps your time format - or the time format used by the by the user under which the job is being run by the task scheduler - is set to single-digit hours, which replaces the leading 0 in the time with a space, so the original ren function sees three arguments, not two.
Of course, f you try to debug this during normal working hours, after morning coffee at 10:00 or later, the time won't contain the space, so it seems to work with your tests.
Wrap the batch file to another one and redirect its complete output to a log file:
winscp_script.bat > c:\writable\path\winscp_script.log
Next day, inspect the log file for any errors.
In general, you should not rely on %TIME% and %DATE% variables, as their format is locale specific. The local account that runs your Windows Scheduler task can have a different locale than the one you use to test the batch file. Not only you get a wrong name, but if the resulting format includes spaces, it would completely break the ren command (as already suggested by #Magoo).
WinSCP itself has a built-in feature for time formatting, so you can do something like:
set TIMESTAMP_FORMAT=hhnnddmmyyyy
pushd "C:\Program Files (x86)\WinSCP"
for /F "tokens=* USEBACKQ" %%F in (
`winscp.com /command "echo %%TIMESTAMP#%TIMESTAMP_FORMAT%%%" "exit"`
) do set TIMESTAMP=%%F
popd
echo %TIMESTAMP%
ren *.csv CL6019_%TIMESTAMP%.csv

How to Lock a file exclusively for 1 minute using windows batch script?

I have a need to lock a file exclusively and continuously keep writing content to it on windows 7.
Objective: While the file is being written with an exclusive lock there is a SFTP schedule that pulls this file from a different server. We need to verify if the file is being pulled partially even if there is a Exclusive Lock on the file.
Used the below batch script but how do i get a lock for 30 seconds to 1 minute?
#echo off
if '%1' == '-lock' (
shift
goto :main
)
call %0 -lock > SAMPLEFILE.csv
goto :eof
:main
ping -n 30 127.0.0.1 > nul
echo %DATE% %TIME% - start
TREE C:\
echo %DATE% %TIME% - finish
goto :eof
There is an exclusive write lock on SAMPLEFILE.csv for the lifetime of the :main routine. The lock is released once the :main routine returns. You can extend the length of the lock by adding a command to delay the return. For example, timeout 60 /nobreak >nul would delay release of the lock by 1 minute. But I don't see how that does you any good.
The lock only prevents other processes from writing to the file. Any process can still read the partial file while it is locked. It is possible to detect if a file is locked by another process, but I don't think that will help with your SFTP server.
I think the simplest thing to prevent partial downloads of the file is to create the file in a folder that the SFTP account(s) cannot access, but on the same volume. When the file is complete, you can instantly move it to the correct location via the MOVE command. The file will be invisible to SFTP until the MOVE is complete, so there would be no risk of partial downloads. Note that this is only instantaneous if moving between two folders within the same volume.
By the way, there is no need for your script to call itself with a -lock argument. You can get the same effect by calling :main directly.
#echo off
call :main %* >SAMPLEFILE.csv
exit /b
:main
ping -n 30 127.0.0.1 > nul
echo %DATE% %TIME% - start
TREE C:\
echo %DATE% %TIME% - finish
exit /b

Writing a simple batch file that checks if a program is running

Me and couple of my friends are using a shared folder on Dropbox, where've uploaded our minecraft server. The point of this is that anyone can launch a server at any time, insted of one person having to run it all the time.
The only problem is, two people might launch the server at the same time, and overlapping save files might occur. To combat this, I want to write a simple batch file.
The logic is this:
IF the process "minecraft_server.1.8.1" is running, rename the the server folder to "RUNNING AS _INSERT_NAME_HERE_", ELSE rename it back to "Minecraft server"
The NAME would be read as a computer name (if that's at all possible), or from some txt file that a user would create (to write their own name)
I've never written a batch file and I don't know if this is possible, but it seems simple enough and any help would be appreciated.
Thank you in advance.
Ok so after a bit of tinkering, I wrote my first batch file. For the most part it seems to be working, however I can't seem to implement a proper loop.
#echo off
IF EXIST *_RUNNING.txt (
echo "ERROR, SERVER ALREADY RUNNING as %computername%"
pause
EXIT
) ELSE (
copy NUL %computername%_RUNNING.txt
START /WAIT minecraft_server.1.8.1.exe
tasklist /FI "IMAGENAME eq javaw.exe" 2>NUL | find /I /N "javaw.exe">NUL
:loop
IF "%ERRORLEVEL%"=="0" (
TIMEOUT /t 5
GOTO loop
) ELSE (
del %computername%_RUNNING.txt
echo "Server ended."
pause
EXIT ) )
When I start minecraft_server.1.8.1.exe it then launches javaw.exe, and that is the actual process that starts the server. After that I just check if the process is still running or not. However I can't seem to loop that particular part of the code, I keep getting synatx errors.
This is not exactly what you are looking for in the topic of the question, but if I get it right you would like to avoid that a server is started twice. So I would do the following:
Create a batch-file that:
First checks if a file named *_MinecraftServer.txt exists. If yes, it assumes that the server is already running and simply exits.
If no such file exists, it creates the file.
Then it starts the server.
Once the server exits, the file is deleted.
This would be something like this:
#echo off
REM Define a filename for the text-file informing other people that a server is running
SET FILENAME=%computername%_MinecraftServer.txt
REM Check if such a file already exists, if not refuse to start:
if exist *_MinecraftServer.txt (
REM Such a file exists, print the content of the file:
echo "ERROR, SERVER ALREADY RUNNING"
type *_MinecraftServer.txt
) else (
REM No such file is found. Create a file, then start the server process.
echo "SERVER is running on %computername%" > %FILENAME%
REM replace the notepad.exe with the name of the server.exe (and all params it requires)
REM Maybe you will also need to write the full-path to the server
notepad.exe
REM Once the server exits, remove the file
del %FILENAME%
echo "Server exited"
)
REM Require the user to hit a key before exiting the batch:
pause
This sample batch starts a notepad.exe and created a file COMPUTERNAME_MinecraftServer.txt.
You would have to replace notepad.exe with the path to the minecraft server exe.
Note that I'm not sure if this batch works if any of the directories involved contains spaces.
And of course this only works if from now on all people start the server only using the batch-file.
Also, theoretically multiple servers can be run if multiple users start the server at more or less the same time, as there is some delay until dropbox will have uploaded and distributed the created txt-file.

Can I prevent a batch file to be executed by more than 1 user at the same time

I have a batch file which can update a web project and clean/rebuild it. I made it executable for network users. Now I want to make the batch executable only by one user at the same time. Like synchronize object in programming languages.
Is there a possibility to do that?
A simple solution to check if batch file is already running is using file system.
The batch file can check if a file exists and denies execution in this case, otherwise it creates the file, runs the commands and finally deletes the file.
#echo off
if exist "C:\Temp\BatchLock.txt" goto BatchRunning
echo Batch file is running by %username%.>C:\Temp\BatchLock.txt
rem All other commands of the batch file
del C:\Temp\BatchLock.txt
goto :EOF
:BatchRunning
type C:\Temp\BatchLock.txt
echo/
echo Please run the batch later again.
echo/
echo Press any key to exit ...
pause >nul
Of course C:\Temp is not a good storage location for the lock text file. It must be a directory which is identical for all users, a directory on server with write permissions for all users.

Undo changes on batch file termination

Is there anyway I can undo all the changes done by a batch file if it is terminated by a user? For example, if I am appending a text file and adding text, renaming files, deleting files by running a batch file... If a user terminates the batch file, is there a way to undo all changes done before it gets terminated by a user? OR anyway that I can not let the user terminate a batch file while its running?
Thanks
You can avoid that the normal user cancel a Batch file with Ctrl-C this way:
#echo off
setlocal
if "%~1" equ "NonCancelable" goto NonCancelable
echo Original!
start "" /B "%~F0" NonCancelable
echo Terminating original...
exit
:NonCancelable
echo I am non cancelable!
echo/
set "var="
set /P "var=Try to cancel me! (enter Exit to end): "
if /I "%var%" neq "exit" goto :NonCancelable
echo Terminating non cancelable...
Of course, the user can click on the window red cross to close the window...
Substantially depends on how sophisticated your users are and to which facilities they have access.
You could for instance create an invisible batch which the user-batch executes. Many examples on SO of how to do this.
Or perhaps you could create new files within the batch, then replace the existing files with the new ones (or even create a replacement directory with a completely new fileset) as the very last operation within your batch.

Resources