Identify running instances of a batch file

*爱你&永不变心* 提交于 2019-12-04 13:47:51

问题


These are not working for me.

Any help to definitelly corret the four examples below ?

The EXAMPLE01 just echoes "continue", even if I have three CMD.exe opened.

---------- EXAMPLE 01 ------------

@echo off 
wmic process where name="cmd.exe" | find "cmd.exe" /c
SET ERRORLEVEL=value if "%value%" GTR 1 ( 
    ECHO This batch is not first  
    ECHO quitting ...
    )
if "%value%" LSS 2 ECHO continue

I am getting the "unexpected i error" message in the EXAMPLE 02!

----------- EXAMPLE 02 -------

@echo off
FOR /F "usebackq tokens=2" %i IN (`tasklist ^| findstr /r /b "cmd.exe"`)
   DO taskkill /pid %%i

I am getting the "is first" message in the EXAMPLE 03, even with three CMD.exe opened!

----------- EXAMPLE 03 -------

 @echo off
    wmic process where name="cmd.exe" | find "cmd.exe" /c
    if "%errorlevel%" LEQ 1 echo CMD is first
    if "%errorlevel%" GTR 1 echo CMD is already running

It is also possible that I will not have access to the Wmic command at work, so, another possibility is found in the EXAMPLE 04 ... but to no avail.

----------- EXAMPLE 04 -------

@echo off
Tasklist /FI "IMAGENAME eq cmd.exe" 2>NUL | find /I /N "cmd.exe">NUL
if "%ERRORLEVEL%"==0 do (goto Use) else (goto Cont)
:Cont
ECHO Only one instance running
pause

:Use
echo Application running already. Close this window

Kind regards, Maleck


回答1:


wmz identified a number of errors in the OP's code, and also has an excellent suggestion to use a lock file for concurrency control.

Below is a robust batch solution that uses a lock file to prevent multiple instances of the batch file from running at the same time. It uses a temporary lock file for the concurrency control. The mere presence of the lock file does NOT stop the script from running. The script will only fail if another process has an exclusive lock on the lock file. This is important in case the script should crash or be killed without deleting the lock file. The next process to run the script will still succeed because the file is no longer locked.

This script assumes the script is installed on a local drive. It allows only one instance for the entire machine. There are lots of variations to control the amount of concurrency allowed. For example, incorporating the %USERNAME% into the lock file name would allow one instance per user in a network environment. Incorporating %COMPUTERNAME% in the name would allow one instance per machine in a network environment.

@echo off
setlocal

:: save parameters to variables here as needed
set "param1=%~1"
:: etc.

:: Redirect an unused file handle for an entire code block to a lock file.
:: The batch file will maintain a lock on the file until the code block
:: ends or until the process is killed. The main code is called from
:: within the block. The first process to run this script will succeed.
:: Subsequent attempts will fail as long as the original is still running.
set "started="
9>"%~f0.lock" (
  set "started=1"
  call :start
)
if defined started (
    del "%~f0.lock" >nul 2>nul
) else (
    echo Process aborted: "%~f0" is already running
)
exit /b

:start
:: The main program appears below
:: All this script does is PAUSE so we can see what happens if
:: the script is run multiple times simultaneously.
pause
exit /b


EDIT

The error message "The process cannot access the file because it is being used by another process." can be suppressed by redirecting stderr output to nul in an outer block.

2>nul (
  9>"%~f0.lock" (
    set "started=1"
    call :start
  )
)

The problem with the above is that all error messages for the main program will also be suppressed. That can be fixed by 1st saving the current definition of stderr to another unused file handle, and then adding yet another inner block that redirects stderr back to the saved file handle.

8>&2 2>nul (
  9>"%~f0.lock" (
    2>&8 (
      set "started=1"
      call :start
    )
  )
)



回答2:


  1. You do not set value anywhere. Even if you did it would not work as variables are expanded on parse. You would need to use delayed expansion if you need to both set and test variable against what was set. Quotes on comparison are not required (see 3). help set will show you info delayed expansion.
  2. %i should be %%i. DO taskkill /pid %%i must be on same line as rest of for command. You also use findstr in regex mode, which means it will search for cmd[any character]exe
  3. You use string (lexical) comparison: "1" leq 1 is true (as "4" leq 1 also is...). Use errorlevel 1 which is equivalent to errorlevel equal or greater than 1. help if shows syntax
  4. Same as 3 plus do in do (goto Use) should be removed. %errorlevel%==0 would work, but it's normally advised to use errorlevel number.

How to check if there is only 1 cmd running:

@echo off
for /f %%i in ('Tasklist /FI "IMAGENAME eq cmd.exe" 2^>NUL' ^| find /I /c  "cmd.exe"') do (
if %%i leq 2 (echo only one instance) else (echo More than one instance)
)

Note: it's just an example. I do not recommend this as a real method of concurrency control. I would rather use lock file for that.



来源:https://stackoverflow.com/questions/11619416/identify-running-instances-of-a-batch-file

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