:: dostuff.bat
@echo off
:: insert long-running process call here
: End
Что можно добавить к этому пакетному файлу, чтобы завершить его, если он уже запущен в другом процессе, когда он выполняется?Отдельный пакетный файл экземпляра?
:: dostuff.bat
@echo off
:: insert long-running process call here
: End
Что можно добавить к этому пакетному файлу, чтобы завершить его, если он уже запущен в другом процессе, когда он выполняется?Отдельный пакетный файл экземпляра?
Ну, если там может быть только один экземпляр точно когда-либо, то вы, вероятно, может сделать это путем создания фиктивного файла где-то, вероятно, в каталоге 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 работает как администратором вы получите «Администратор: DoStuff» в качестве названия. Который больше не соответствует tasklist
. Вы можете взломать его, также проверяя «Administrator: DoStuff», но это может выглядеть по-другому в зависимости от языка пользовательского интерфейса Windows, поэтому это, вероятно, не лучшее решение.
Невозможно сделать это чистым способом без использования специального внешнего инструмента (вы хотите что-то, что создает мьютекс И запускает (и ждет) вашу внешнюю команду, таким образом, если процесс будет убит, мьютекс умирает вместе с ним)
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++ помощник приложение:
//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;
}
ДВА ФАЙЛА:
-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"
Пуск-> Выполнить:.
эхо CMD/к -^| -> - летучая мышь & -
Нарушает правила потому что (много) более одного экземпляра запускается одновременно, но это забавный способ получить бесплатную еду: поставите на обед своего коллеги, чтобы он не мог убить его без холодной загрузки.
В основном вы не можете на виду.
Во-первых, потому что это не так. Вы должны найти правильный один процесс 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% зависит от формата даты и времени на странице локализации Windows.
Перенаправление по потоку id 9 используется для блокировки файла в каталоге для записи и поэтому блокирует сам родительский каталог. Если поток по какой-либо причине не может быть перенаправлен, тогда скрипт немедленно прекращается с помощью cmd.exe без дальнейшего выполнения, которого достаточно для взаимного исключения исполнения. Поэтому, поскольку перенаправление происходит до исполнения, нет страха, что что-то может выполнить перед перенаправлением.
Если произошел сбой или ctrl-c, то каталог останется на диске (hdd, ssd, который когда-либо еще), поэтому для его очистки в этом случае скрипт пытается удалить весь каталог со всеми файлами внутри перед mkdir.
Единственное, что может случиться здесь неправильно, это то, что никто не может получить исполнение, которое кажется не так часто случаться. По крайней мере, это лучше, чем «более одного за раз».
Идея списка задач великолепна. Спасибо. Как написать тест на выход этой команды? То есть, когда я запускаю список задач в batchfile, какая логика знать, сообщила ли она мне, что пакетный файл уже запущен или нет? – lance
Хм, извините за этот список задач. Я думал, что tasklist ответит кодом выхода; вы могли бы использовать их с 'if errorlevel ...', но tasklist всегда возвращает 0, независимо от того, было ли обнаружено окно или нет. Это отстой. Вы все равно можете записать вывод с помощью 'for/f', но я бы посоветовал ему, так как это по своей сути хрупкое (локализация и прочее). – Joey
Справа. Когда это не сработало, я уволил свой вопрос. Я могу передать результаты списка задач в findstr, который возвращает условный уровень ошибок. Мне это не нравится, но я никогда не занимаюсь эстетикой, когда я создаю пакетные файлы (особенно для личного использования, как в этом случае). – lance