单实例batch file?

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

我可以添加到这个batch file,如果它已经在执行时在另一个进程中运行,它会终止?

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

 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编辑或者崩溃的批处理文件来说,这个标题也应该更加强大。

设置标题:

 title DoStuff 

事后重新设置:

 title Not doing stuff 

检查它:

 tasklist /FI "WINDOWTITLE eq DoStuff" 

但是,这有一个问题:当cmd以管理员身份运行时,您将获得“Administrator:DoStuff”作为标题。 这与tasklist不匹配。 你也可以通过检查“Administrator:DoStuff”来解决这个问题,但是这可能看起来不同,这取决于Windows UI语言,所以它可能不是最好的解决方案。

如果不使用自定义的外部工具(你想要创建一个互斥体并且运行(并等待)你的外部命令,这样,如果进程被终止,互斥体就会死掉)

批量:

 @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; } 

你大多不能以实物的方式。

首先是因为这不是微不足道的事情。 你一定要找到正确的一个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用于为目录中的文件指定唯一的名称。 由于%DATE%和%TIME%变量的格式取决于窗口本地化页面中日期和时间的格式,因此使用所有这些对特定字符的替换。

通过流ID 9重定向用于锁定目录中的文件进行写操作,从而将父目录本身锁定在删除状态。 如果流由于某种原因不能被重定向,那么脚本会立即终止,cmd.exe将不进行进一步的执行,这足以实现互斥。 所以,因为重定向发生在执行之前,所以不用担心在重定向之前可以执行某些操作。

如果crash或者ctrl-c发生了,那么这个目录就会保留在存储器(hdd,ssd,还有其他地方),所以在这种情况下,脚本尝试删除mkdir之前的所有文件。

这里唯一可能发生的错误是,没有人可能获得似乎不太经常发生的执行。 至少比“一次多一个”要好。

两个文件:

 -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" 

开始 – >运行:

echo cmd / k – ^ | – > – 。bat& –

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