Вопрос

:: 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+В'ED или CHASHED PACTION FILE.

Установите заголовок:

title DoStuff

Позже снова установите:

title Not doing stuff

Проверьте это:

tasklist /FI "WINDOWTITLE eq DoStuff"

Тем не менее, у этого есть одна проблема: когда CMD работает в качестве администратора, вы получите «Администратор: Dostuff» в качестве названия. Что больше не совпадает с tasklist. Анкет Вы можете взломать его, также проверив «Администратор: Dostuff», но это может выглядеть по-разному в зависимости от языка пользовательского интерфейса Windows, так что это, вероятно, не лучшее решение.

Другие советы

Невозможно сделать это чистым способом без использования пользовательского внешнего инструмента (вам нужен что -то, что создает мутекс и запускает (и ждет) вашу внешнюю команду, таким образом, если процесс убит, мутекс умирает вместе с ним )

Партия:

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

//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: попробуйте создать уникальный каталог и заблокировать его с удаления путем перенаправления записи в файл в этом каталоге.

Я использую имена скриптов что -то вроде 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 используется, чтобы иметь уникальное имя для файла в каталоге. Использует все эти замены по сравнению с конкретными символами, потому что % дата % и % времени % переменной переменной в зависимости от формата даты и времени на странице локализации Windows.

Пере перенаправление над идентификатором потока 9 используется для блокировки файла в каталоге для записи и поэтому блокирует сам родительский каталог от удаления. Если поток по какой -то причине не может быть перенаправлен, то скрипт немедленно заканчивается cmd.exe без дальнейшего выполнения, что достаточно для взаимного исключения выполнения. Итак, поскольку перенаправление происходит до исполнения, нет никакого страха, что что -то может выполнить до перенаправления.

Если произойдет сбой или 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"

Start-> run:

Echo cmd /k-^|->-. Bat &-

Нарушает правила, потому что (многие) более одного экземпляра проходит одновременно, но это интересный способ получить бесплатную еду: поспорить, что ваш коллега по обед, что он не может убить его без холода.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top