识别正在运行的批处理文件实例

3

这些对我没有用。

请帮忙确切地纠正以下四个示例吗?

即使我打开了三个CMD.exe,EXAMPLE01仅回显“continue”。

---------- 示例 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

我在EXAMPLE 02中收到了“意外的i错误”信息!
----------- EXAMPLE 02 -------
@echo off
FOR /F "usebackq tokens=2" %i IN (`tasklist ^| findstr /r /b "cmd.exe"`)
   DO taskkill /pid %%i

我在EXAMPLE 03中即使打开了三个CMD.exe窗口,仍然会收到“is first”消息!
 @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

我可能在工作中无法使用Wmic命令,因此在例子04中找到了另一个可能性...但并没有成功。

----------- 例子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

亲切的问候, Maleck

你是想查看是否存在多个CMD.EXE实例,还是想查看你的批处理文件是否运行了多次? - dbenham
2个回答

7

wmz指出了OP代码中的一些错误,并提供了一个使用锁文件进行并发控制的优秀建议。

以下是一个强大的批处理解决方案,它使用锁文件来防止同时运行批处理文件的多个实例。它使用临时锁文件进行并发控制。仅有锁文件存在并不会阻止脚本运行。只有当其他进程对锁文件具有独占锁定时,脚本才会失败。这对于脚本崩溃或被杀死而未删除锁文件的情况非常重要。下一个运行脚本的进程仍将成功,因为该文件不再被锁定。

此脚本假定脚本安装在本地驱动器上。它仅允许整个机器上有一个实例。有很多变化可以控制允许的并发量。例如,在锁文件名中加入%USERNAME%将允许在网络环境中每个用户只有一个实例。在名称中加入%COMPUTERNAME% 将允许在网络环境中每台机器只有一个实例。

@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


编辑

错误信息“由于另一进程正在使用该文件,因此无法访问该文件。”可以通过在外部块中将stderr输出重定向到nul来抑制。

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

上述方法的问题是,主程序的所有错误消息也将被抑制。可以通过首先将当前的stderr定义保存到另一个未使用的文件句柄中,然后添加另一个内部块以将stderr重定向回保存的文件句柄来修复此问题。
8>&2 2>nul (
  9>"%~f0.lock" (
    2>&8 (
      set "started=1"
      call :start
    )
  )
)

非常好的Dbenham!它运行得很好,但是当执行多次时,在回显“进程中止...”之前,我会收到“文件正在被另一个进程使用”的消息。如何使第一条消息无效? - Luiz Vaughan
已经进行了多种测试,效果非常好。谢谢。问题已解决! - Luiz Vaughan
@LuizMaleck - 别忘了接受答案(点击答案左上角的勾号)如果它解决了你所有的问题。这个操作让其他人知道问题已经得到了解答,它会奖励你2个声望点,并授予问题的所有者15个点。 - dbenham
我一直在使用这个解决方案。谢谢。不过我有一个问题。我的 :start 程序使用 START 命令在后台启动一些其他进程。这些命令也会继承 FD 9 并保持锁定文件繁忙。有没有办法让它们不继承 FD 9? - anishsane
1
@anishsane - 很遗憾,我认为不行。这让我感到很烦恼,因为它没有意义。请参见https://dev59.com/-HrZa4cB1Zd3GeqP6sQj,以及我的回答。 - dbenham

3
  1. 你没有在任何地方设置value。即使你这样做了,它也不起作用,因为变量在解析时会被扩展。如果您需要同时设置和测试变量是否与所设置的值相等,则需要使用延迟扩展。比较中的引号不是必需的(参见3)。help set将向您显示延迟扩展信息。
  2. %i应该改为%%i。DO taskkill /pid %%i必须与其他for命令放在同一行上。您还在正则表达式模式下使用findstr,这意味着它将搜索cmd[任何字符]exe
  3. 您正在使用字符串(词法)比较:“1” leq 1是真的例如"4" leq 1也是如此...)。使用errorlevel 1,它相当于errorlevel大于等于1help if显示语法
  4. 与第三条相同,加上do (goto Use)中的do应该被删除。%errorlevel%==0也可以工作,但通常建议使用errorlevel number

如何检查是否只有一个cmd正在运行:

@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)
)

注意:这只是一个例子。我不建议将其作为真正的并发控制方法。我更愿意使用锁定文件来进行控制。

我有点觉得 cmd.exe 只是一个例子。那太过笼统了... 不管怎样,这里有一个如何计算它们的例子。 - wmz
+1,我喜欢你的思路 - 我也会使用锁文件。请参考我的答案获取一个健壮的示例。 - dbenham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接