After terminate the batch needs to delete a .lock file that has been create

匿名 (未验证) 提交于 2019-12-03 01:18:02

问题:

Hello I am very new in this and I was able to run one instance of a batch file.

However, another file is being created called something.lock but the file is not been deleted by itself when I stop the batch or close it. The new file create is the one that helps to have one instance running. Can the new file ".lock " be deleted after I close the script with the "X" or because an user ended correctly with going to label end:

The code that I have is

:init set "started=" 2>nul (  9>"%~f0.lock" (   set "started=1"   call :start  ) ) @if defined started (     del "%~f0.lock"  ) else (     cls     ECHO                         Only one instance is allowed     timeout /NOBREAK /T 3 >nul    cls ) exit /b :start cd /d %~dp0 cls :initial pause >nul 

回答1:

You are misapplying the lock file. You are simply checking to see if the file exists, which means you must guarantee that the file is deleted upon batch termination.

There is a much better way, which you have only partially implemented. Only one process can have the file open for write access. You just need to determine if the file is already locked by another process.

Once the process with the exclusive lock terminates, the lock will be released. This is true no matter how the script terminates - even if it was the result of Ctrl-C or window closure. The file might not be deleted, but the next time the script runs, the file won't be locked, so the script will proceed nicely.

In the code below I save the current definition of stderr to an unused file handle before I redirect sterr to nul. Within the inner block I redirect stderr back to the saved definition. In this way I prevent the error message if the file is already locked, but the CALLed :start routine will still print out error messages normally.

@echo off :init 8>&2 2>nul ( 2>&8 9>"%~f0.lock" call :start ) || (   cls   ECHO                         Only one instance is allowed   timeout /NOBREAK /T 3 >nul   cls ) del "%~f0.lock" 2>nul exit /b  :start cd /d %~dp0 cls del asdfasdfasdf :initial pause >nul 


回答2:

The difficulty is that your batch thread itself won't have its own PID. There's no graceful way to tell whether your batch script is running or when it has terminated. And there's no way to wake the dead, to let the script have the last word when a user red X's or Ctrl+C's. When it's over, it's over.

There are a few ways you can do what you want to do. Try them all and see which you prefer. Use dbenham's solution. His is correct. The following efforts are left here as an exercise in futility, although Solution 4 seems to work very well. In the end, it's still just a hack; whereas dbenham's redirection sleight-of-hand provides a correct implementation of lock files the way lock files are supposed to work.

...

Solution 1

One simple way is to use powershell to minimize the current window, re-launch your script with start /wait, then after completion call powershell again to restore.

@echo off setlocal  set "lock=%temp%\~%~n0.lock"  if "%~1" neq "wrapped" (      if exist "%lock%" (         echo Only one instance is allowed.         timeout /nobreak /t 3 >NUL         exit /b     )      rem :: create lock file     >"%lock%" echo 1      rem :: minimize this console     powershell -windowstyle minimized -command ""      rem :: relaunch self with "wrapped" argument and wait for completion     start /wait "" cmd /c "%~f0" wrapped      rem :: delete lock file     del "%lock%"      rem :: restore window     powershell -windowstyle normal -command ""      goto :EOF  )  :: Main script goes here. :loop cls echo Simulating script execution... ping -n 2 0.0.0.0 >NUL goto loop 

This should be enough for casual use and should account for any cause of the batch file's termination short of taskkill /im "cmd.exe" /f or a reboot or power outage.


Solution 2

If you need a more bulletproof solution, you can get the current console window's PID and intermittently test that it still exists. start /min a helper window to watch for its parent window to die, then delete the lock file. And as long as you're creating a watcher anyway, might as well let that watcher be the lock file.

Biggest drawback to this method is that it requires to end your main script with exit to destroy the console window, whether you want it destroyed or not. There's also a second or two pause while the script figures out its parent's PID.

(Save this with a .bat extension and run it as you would any other batch script.)

@if (@a==@b) @end   /* JScript multiline comment  :: begin batch portion  @echo off setlocal  :: locker will be a batch script to act as a .lock file set "locker=%temp%\~%~nx0"  :: If lock file already exists if exist "%locker%" (     tasklist /v | find "cleanup helper" >NUL && (         echo Only one instance allowed.         timeout /nobreak /t 3 >NUL         exit /b     ) )  :: get PID of current cmd console window for /f "delims=" %%I in ('cscript /nologo /e:Jscript "%~f0"') do (     set "PID=%%I" )  :: Create and run lock bat. >"%locker%" echo @echo off >>"%locker%" echo setlocal >>"%locker%" echo echo Waiting for parent script to finish... >>"%locker%" echo :begin >>"%locker%" echo ping -n 2 0.0.0.0^>NUL >>"%locker%" echo tasklist /fi "PID eq %PID%" ^| find "%PID%" ^>NUL ^&^& ^( >>"%locker%" echo   goto begin >>"%locker%" echo ^) ^|^| ^( >>"%locker%" echo   del /q "%locker%" ^&^& exit >>"%locker%" echo ^)  :: Launch cleanup watcher to catch ^C start /min "%~nx0 cleanup helper" "%locker%"  :: ================== :: Rest of script :: blah :: blah :: blah :: ==================  :end echo Press any key to close this window. pause >NUL exit  :: end batch portion / begin JScript :: https://stackoverflow.com/a/27514649/1683264 :: */  var oShell = WSH.CreateObject('wscript.shell'),     johnConnor = oShell.Exec('%comspec% /k @echo;');  // returns PID of the direct child of explorer.exe function getTopPID(PID, child) {     var proc = GetObject("winmgmts:Win32_Process=" + PID);     return (proc.name == 'explorer.exe') ? child : getTopPID(proc.ParentProcessID, PID); }  var PID = getTopPID(johnConnor.ProcessID); johnConnor.Terminate();  // output PID of console window WSH.Echo(PID); 

Solution 3

You can also test a lock file and see whether it's stale by setting a timestamp within the lock file, and setting that same timestamp in your console window title. Only problem with this is that the window title doesn't revert to normal if the user terminates with Ctrl+C, so you can't run the script twice without closing the cmd window. But closing the window and opening a new one for subsequent launches may not be too terrible a price to pay, as this is the simplest method described thusfar.

@echo off setlocal  set "started=%time%" set "lockfile=%temp%\~%~n0.lock"  if exist "%lockfile%" (     NUL && (     echo Only one instance allowed.     timeout /nobreak /t 3 >NUL     exit /b )  title %~nx0 started at %started% >"%lockfile%" echo %started%  :: rest of script here  echo Simulating script execution... :loop ping -n 2 0.0.0.0 >NUL goto loop 

Solution 4

Here's a bit more polished solution, combining methods 1 and 3. It re-launches itself in the same window, then sets the window title to a unique ID. When the script exits gracefully, the lock file is deleted. Whether the script exits gracefully or forcefully, the window title reverts back to its default. And if no window exists in the task list with a title matching the unique ID, the lock file is deemed stale and is overwritten. Otherwise, the script notifies the user that only one instance is allowed and exits. This is my favorite solution.

@echo off setlocal  if "%~1" neq "wrapped" (     cmd /c "%~f0" wrapped %*     goto :EOF ) :: remove "wrapped" first argument shift /1  :: generate unique ID string >"%temp%\~%~n0.a" echo %date% %time% >NUL certutil -encode "%temp%\~%~n0.a" "%temp%\~%~n0.b" for /f "usebackq EOL=- delims==" %%I in ("%temp%\~%~n0.b") do set "running_id=%%I" del "%temp%\~%~n0.a" "%temp%\~%~n0.b"  set "lockfile=%temp%\~%~n0.lock"  if exist "%lockfile%" (     NUL && (     echo Only one instance allowed.     timeout /nobreak /t 3 >NUL     exit /b )  title %running_id% >"%lockfile%" echo %running_id%  :: rest of script here  echo Press any key to exit gracefully, or Ctrl+C to break pause >NUL  del "%lockfile%" goto :EOF 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!