windows 单实例批处理文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2110495/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Single instance batch file?
提问by lance
:: dostuff.bat
@echo off
:: insert long-running process call here
: End
What can I add to this batch file to make it terminate if it's already running in another process when it's executed?
如果它在执行时已经在另一个进程中运行,我可以向该批处理文件添加什么以使其终止?
回答by Joey
Well, if there can be only exactly one instance ever, then you can probably do this by creating a dummy file somewhere, probably in the temp directory:
好吧,如果永远只能有一个实例,那么您可以通过在某处创建一个虚拟文件来做到这一点,可能是在临时目录中:
copy NUL "%TEMP%\dostuff_is_doing_stuff.tmp"
you then remove it after you're done:
然后在完成后将其删除:
del "%TEMP%\dostuff_is_doing_stuff.tmp"
and at the start of the batch file you can check whether that file exists and exit accordingly:
在批处理文件的开头,您可以检查该文件是否存在并相应地退出:
if exist "%TEMP%\dostuff_is_doing_stuff.tmp" (
echo DoStuff is already running. Exiting ...
exit /b 1
)
Similarly, you can do that by changing the window title, which should also be more robust against a Ctrl+C'ed or crashed batch file.
同样,您可以通过更改窗口标题来做到这一点,这对于Ctrl+ C'ed 或崩溃的批处理文件也应该更加健壮。
Set the title:
设置标题:
title DoStuff
Re-set it afterwards:
之后重新设置:
title Not doing stuff
Check for it:
检查它:
tasklist /FI "WINDOWTITLE eq DoStuff"
However, this has one problem: When cmd runs as administrator you'll get "Administrator: DoStuff" as the title. Which doesn't match anymore with tasklist
. You can hack-solve it by also checking for "Administrator: DoStuff" but that might look different depending on the Windows UI language, so it's probably not the best solution.
但是,这有一个问题:当 cmd 以管理员身份运行时,您将获得“管理员:DoStuff”作为标题。不再与tasklist
. 您还可以通过检查“管理员:DoStuff”来破解它,但这可能会因 Windows UI 语言而异,因此它可能不是最佳解决方案。
回答by Anders
There is no way to do this in a clean way without using a custom external tool (You want something that creates a mutex AND runs (and waits for) your external command, this way, if the process is killed, the mutex dies with it)
如果不使用自定义的外部工具,则无法以干净的方式执行此操作(您想要创建互斥锁并运行(并等待)您的外部命令的东西,这样,如果进程被终止,互斥锁也会随之死亡)
Batch:
批:
@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++ helper app:
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;
}
回答by Andry
You mostly can't in kind way.
你大多不能以善意的方式。
First of because it is not trivial to do. You have to find the correct one cmd.exe process when in general it is not quite easy to "do all things right".
首先是因为它不是微不足道的。您必须找到正确的 cmd.exe 进程,因为一般来说“把所有事情都做对”并不容易。
Second, termination may be is not enough to stop the script, because one script can run another script in cycle, so just terminating one will not lead to complete termination of another. More over, it can lead to a broken execution because you have to terminate the whole process tree from the root "atomically" to properly stop the execution.
其次,终止可能不足以停止脚本,因为一个脚本可以循环运行另一个脚本,所以仅仅终止一个不会导致另一个完全终止。此外,它可能会导致执行中断,因为您必须“以原子方式”从根部终止整个流程树才能正确停止执行。
Instead of "search to terminate" you can just "don't execute if already running". The technique is the same as in unix shell scripting: try to create some unique directory and lock it from removing by write redirection into a file in that directory.
而不是“搜索终止”,您可以“如果已经运行则不执行”。该技术与 unix shell 脚本中的相同:尝试创建一些唯一的目录并通过写入重定向到该目录中的文件来锁定它不被删除。
i am using the script names something like exec_once_or_exit.bat:
我使用的脚本名称类似于 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
Usage:
用法:
exec_once_or_exit.bat <UniqueNameForLock> <PathToExecutable> <8Arguments>
exec_once_or_exit.bat <UniqueNameForLock> <BatchCommand(s)>
Explanation:
解释:
The mkdir command will return non zero exit code if directory already exists or can't be created for some reason, and 0 if created.
如果目录已经存在或由于某种原因无法创建,则 mkdir 命令将返回非零退出代码,如果创建则返回 0。
The LOCK_DIR_NAME_SUFFIX is used to have unique name for the file in the directory. Is uses all these replacements over specific characters because %DATE% and %TIME% variable's format depending on the format of date and time in the windows localization page.
LOCK_DIR_NAME_SUFFIX 用于为目录中的文件提供唯一名称。因为 %DATE% 和 %TIME% 变量的格式取决于 Windows 本地化页面中的日期和时间格式,所以对特定字符使用所有这些替换。
Redirection over the stream id 9 is used to lock the file in directory for write and so locks the parent directory itself from removing. If the stream for some reason can't be redirected then the script terminates immediately by the cmd.exe w/o further execution which is enough for the mutual exclusion of execution. So, because the redirection happens before the execution there is no fear that something can execute before the redirection.
流 id 9 上的重定向用于将文件锁定在目录中以进行写入,从而锁定父目录本身不被删除。如果由于某种原因无法重定向流,则脚本立即由 cmd.exe 终止,无需进一步执行,这足以相互排斥执行。因此,因为重定向发生在执行之前,所以不必担心在重定向之前可以执行某些操作。
If crash or ctrl-c is happened then the directory will remain on the storage (hdd, ssd, that ever else), so to clean up it in that case the script tryes to remove the whole directory with all files inside before the mkdir.
如果发生崩溃或 ctrl-c,则该目录将保留在存储上(hdd、ssd 等),因此要在这种情况下清理它,脚本会尝试在 mkdir 之前删除包含所有文件的整个目录。
The only thing what can happen here wrong is that the no one may acquire the execution which seems not so frequent to happen. At least it is better than "more than one at a time".
这里唯一可能发生的错误是没有人可能获得似乎不那么频繁发生的执行。至少它比“一次多个”要好。
回答by untitled
The best, simple, charming way to do it:
最好的,简单的,迷人的方式来做到这一点:
@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
回答by Nelson Costa
TWO FILES:
两个文件:
-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-
-命令.BAT-
REM ...do something
REM ...do something
REM ...do something
DEL "%TEMP%\dostuff_is_doing_stuff.tmp"
回答by druid
Start->Run:
开始->运行:
echo cmd /k -^|->-.bat&-
echo cmd /k -^|->-.bat&-
Violates the rules because (many) more than one instance runs simultaneously- but it's a fun way to get free food: bet your coworker lunch that he can't kill it without cold-booting.
违反规则,因为(许多)多个实例同时运行 - 但这是一种获得免费食物的有趣方式:打赌你的同事午餐他不能在没有冷启动的情况下杀死它。