2
:: dostuff.bat
@echo off
:: insert long-running process call here
: End

如果它在执行时已经在另一个进程中运行,我可以向这个批处理文件添加什么以使其终止?

4

6 回答 6

8

好吧,如果只能有一个实例,那么您可以通过在某个地方创建一个虚拟文件来做到这一点,可能在 temp 目录中:

copy NUL "%TEMP%\dostuff_is_doing_stuff.tmp"

完成后将其删除:

del "%TEMP%\dostuff_is_doing_stuff.tmp"

并且在批处理文件的开头,您可以检查该文件是否存在并相应地退出:

if exist "%TEMP%\dostuff_is_doing_stuff.tmp" (
    echo DoStuff is already running. Exiting ...
    exit /b 1
)

Ctrl同样,您可以通过更改窗口标题来做到这一点,这对于+ C'ed 或崩溃的批处理文件也应该更加健壮。

设置标题:

title DoStuff

之后重新设置:

title Not doing stuff

检查它:

tasklist /FI "WINDOWTITLE eq DoStuff"

但是,这有一个问题:当 cmd 以管理员身份运行时,您将获得“Administrator: DoStuff”作为标题。哪个不再匹配tasklist. 您也可以通过检查“管理员:DoStuff”来破解它,但根据 Windows UI 语言的不同,这可能看起来不同,因此它可能不是最佳解决方案。

于 2010-01-21T15:46:12.313 回答
4

如果不使用自定义外部工具,则无法以干净的方式执行此操作(您想要创建互斥锁并运行(并等待)您的外部命令的东西,这样,如果进程被杀死,互斥锁也会随之死亡)

批:

@echo off
echo starting long running process
REM calling dir here just to get a long running operation
onecmd.exe cmd.exe /C dir /S /B \*
echo done...bye

C++ 助手应用程序:

//Minimal error checking in this sample ;)
#include <Windows.h>
#include <TCHAR.h>

int main()
{
    const TCHAR myguid[]=_T("50D6C9DA-8135-42de-ACFE-EC223C214DD7");
    PROCESS_INFORMATION pi;
    STARTUPINFO si={sizeof(STARTUPINFO)};

    HANDLE mutex=CreateMutex(NULL,true,myguid);
    DWORD gle=GetLastError();
    if (!mutex || gle==ERROR_ALREADY_EXISTS) 
    {
        CloseHandle(mutex);
        return gle;
    }

    TCHAR*p=GetCommandLine();
    if (*p=='"' && *(++p)) {
        while (*p && *p!='"')++p;if (*p)++p;
    }
    else 
        while (*p && *p!=' ')++p;
    while(*p==' ')++p;

    if (CreateProcess(0,p,0,0,false,0,0,0,&si,&pi)) 
    {
        DWORD ec;
        WaitForSingleObject(pi.hProcess,INFINITE);
        GetExitCodeProcess(pi.hProcess,&ec);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        return ec;
    }
    gle=GetLastError();
    CloseHandle(mutex);
    return gle;
}
于 2010-01-23T08:00:44.717 回答
1

你大多不能以善良的方式。

首先是因为它不是微不足道的。您必须找到正确的一个 cmd.exe 进程,因为通常“把所有事情都做对”并不容易。

其次,终止可能不足以停止脚本,因为一个脚本可以循环运行另一个脚本,所以仅仅终止一个不会导致另一个完全终止。此外,它可能导致执行中断,因为您必须从根“原子地”终止整个进程树才能正确停止执行。

而不是“搜索终止”,您可以“如果已经运行则不执行”。该技术与 unix shell 脚本中的技术相同:尝试创建一些唯一目录并通过将重定向写入该目录中的文件来锁定它以防止将其删除。

我正在使用类似 exec_once_or_exit.bat 的脚本名称:

@echo off

setlocal

set "LOCK_DIR_NAME_SUFFIX=%DATE%_%TIME%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX::=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:/=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:-=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:.=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:,=_%"
set LASTERROR=0

rem cleanup if leaked by crash or ctrl-c, won't be removed if already acquired because of write redirection lock
rmdir /S /Q "%TEMP%\lock_%~1" >nul 2>&1

mkdir "%TEMP%\lock_%~1" && (
  rem IMPL to use "goto :EOF" in next command instead of "exit /b %ERRORLEVEL%" under "block" command - "( )"
  call :IMPL %%*
  goto :EOF
)
exit /b -1024

:IMPL
rem call IMPL2 to recover exit code from commands like "exit /b"
call :IMPL2 %%* 9> "%TEMP%\lock_%~1\lock0_%LOCK_DIR_NAME_SUFFIX%.txt"
set LASTERROR=%ERRORLEVEL%
rmdir /S /Q "%TEMP%\lock_%~1"
exit /b %LASTERROR%

:IMPL2
rem a batch script must be always call over the "call" prefix
if "%~n2" == "bat" call %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9 && goto :EOF
if not "%~n2" == "bat" (
  %2 %3 %4 %5 %6 %7 %8 %9
)
goto :EOF

用法:

exec_once_or_exit.bat <UniqueNameForLock> <PathToExecutable> <8Arguments>
exec_once_or_exit.bat <UniqueNameForLock> <BatchCommand(s)>

解释:

如果目录已经存在或由于某种原因无法创建,mkdir 命令将返回非零退出代码,如果已创建则返回 0。

LOCK_DIR_NAME_SUFFIX 用于为目录中的文件提供唯一名称。Is 对特定字符使用所有这些替换,因为 %DATE% 和 %TIME% 变量的格式取决于 Windows 本地化页面中日期和时间的格式。

流 id 9 上的重定向用于锁定目录中的文件以进行写入,从而锁定父目录本身以防止删除。如果由于某种原因无法重定向流,则脚本会立即由 cmd.exe 终止,无需进一步执行,这足以实现互斥执行。所以,因为重定向发生在执行之前,所以不用担心在重定向之前可以执行某些东西。

如果发生崩溃或 ctrl-c,则该目录将保留在存储中(hdd、ssd 等),因此在这种情况下要清理它,脚本会尝试删除整个目录以及 mkdir 之前的所有文件。

这里唯一可能发生的错误是没有人可能会获得似乎不那么频繁发生的执行。至少比“一次不止一个”要好。

于 2015-05-10T14:25:34.827 回答
1

最好、简单、迷人的方法:

@echo off
set /a count=0
for /f "skip=3 USEBACKQ delims=" %%a in (`wmic process where "name='cmd.exe' and commandline like '%%%~nx0%%'" get processid`) do set /a count+=1
if %count% gtr 1 exit
echo 我要运行, Please run me....
pause
于 2019-12-19T06:40:29.157 回答
0

两个文件:

-START.BAT-

if exist "%TEMP%\dostuff_is_doing_stuff.tmp" (   
exit /b 1
)

copy NULL "%TEMP%\dostuff_is_doing_stuff.tmp"

cmd /K COMMANDS.bat

-COMMANDS.BAT-

REM ...do something

REM ...do something

REM ...do something    

DEL "%TEMP%\dostuff_is_doing_stuff.tmp"
于 2013-06-15T14:02:34.877 回答
0

开始->运行:

echo cmd /k -^|->-.bat&-

违反规则是因为(许多)多个实例同时运行 - 但这是获得免费食物的一种有趣方式:打赌你的同事午餐,他不能在没有冷启动的情况下杀死它。

于 2014-02-22T03:31:22.857 回答